Skip to content

Commit 9792a43

Browse files
authored
Doc rework & Any (#473)
* More docs rework * Tweak docs some more * Unstructuring `Any` * Tweak changelog * Doc improvements * Tweak cattrs.gen __all__ * Prune docs
1 parent 8166add commit 9792a43

24 files changed

Lines changed: 777 additions & 1083 deletions

HISTORY.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
## 24.1.0 (UNRELEASED)
66

7+
- **Potentially breaking**: Unstructuring hooks for `typing.Any` are consistent now: values are unstructured using their runtime type.
8+
Previously this behavior was underspecified and inconsistent, but followed this rule in the majority of cases.
9+
([#473](https://github.com/python-attrs/cattrs/pull/473))
710
- Introduce {meth}`BaseConverter.get_structure_hook` and {meth}`BaseConverter.get_unstructure_hook` methods.
811
([#432](https://github.com/python-attrs/cattrs/issues/432) [#472](https://github.com/python-attrs/cattrs/pull/472))
912
- The default union handler now properly takes renamed fields into account.
@@ -26,6 +29,8 @@
2629
- Tests are run with the pytest-xdist plugin by default.
2730
- Rework the introductory parts of the documentation, introducing the Basics section.
2831
([#472](https://github.com/python-attrs/cattrs/pull/472))
32+
- The documentation has been significantly reworked.
33+
([#473](https://github.com/python-attrs/cattrs/pull/473))
2934
- The docs now use the Inter font.
3035

3136
## 23.2.3 (2023-11-30)

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# cattrs
22

3+
<p>
4+
<em>Great software needs great data structures.</em>
5+
</p>
6+
37
<a href="https://pypi.python.org/pypi/cattrs"><img src="https://img.shields.io/pypi/v/cattrs.svg"/></a>
48
<a href="https://github.com/python-attrs/cattrs/actions?workflow=CI"><img src="https://github.com/python-attrs/cattrs/workflows/CI/badge.svg"/></a>
59
<a href="https://catt.rs/en/latest/?badge=latest"><img src="https://readthedocs.org/projects/cattrs/badge/?version=latest" alt="Documentation Status"/></a>
@@ -103,7 +107,7 @@ When you're done, `unstructure` the data to its unstructured form and pass it al
103107
Use [attrs type metadata](http://attrs.readthedocs.io/en/stable/examples.html#types) to add type metadata to attributes, so _cattrs_ will know how to structure and destructure them.
104108

105109
- Free software: MIT license
106-
- Documentation: https://catt.rs
110+
- Documentation: [https://catt.rs](https://catt.rs)
107111
- Python versions supported: 3.8 and up. (Older Python versions are supported by older versions; see the changelog.)
108112

109113
## Features

docs/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,9 @@ pseudoxml:
171171
@echo
172172
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
173173

174+
.PHONY: apidoc
174175
apidoc:
175-
pdm run sphinx-apidoc -o . ../src/cattrs/ -f
176+
pdm run sphinx-apidoc -o . ../src/cattrs/ '../**/converters.py' -f -M
176177

177178
## htmlview to open the index page built by the html target in your browser
178179
.PHONY: htmlview

docs/basics.md

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@
33
```
44

55
All _cattrs_ functionality is exposed through a {class}`cattrs.Converter` object.
6-
A global converter is provided for convenience as {data}`cattrs.global_converter` but more complex customizations should be performed on private instances.
6+
A global converter is provided for convenience as {data}`cattrs.global_converter` but more complex customizations should be performed on private instances, any number of which can be made.
77

88

9-
## Converters
9+
## Converters and Hooks
1010

11-
The core functionality of a converter is [structuring](structuring.md) and [unstructuring](unstructuring.md) data by composing provided and [custom handling functions](customizing.md), called _hooks_.
11+
The core functionality of a converter is structuring and unstructuring data by composing [provided](defaulthooks.md) and [custom handling functions](customizing.md), called _hooks_.
1212

1313
To create a private converter, instantiate a {class}`cattrs.Converter`. Converters are relatively cheap; users are encouraged to have as many as they need.
1414

15-
The two main methods are {meth}`structure <cattrs.BaseConverter.structure>` and {meth}`unstructure <cattrs.BaseConverter.unstructure>`, these are used to convert between _structured_ and _unstructured_ data.
15+
The two main methods, {meth}`structure <cattrs.BaseConverter.structure>` and {meth}`unstructure <cattrs.BaseConverter.unstructure>`, are used to convert between _structured_ and _unstructured_ data.
1616

1717
```python
1818
>>> from cattrs import structure, unstructure
@@ -28,53 +28,54 @@ The two main methods are {meth}`structure <cattrs.BaseConverter.structure>` and
2828
Model(a=1)
2929
```
3030

31-
_cattrs_ comes with a rich library of un/structuring rules by default, but it excels at composing custom rules with built-in ones.
31+
_cattrs_ comes with a rich library of un/structuring hooks by default but it excels at composing custom hooks with built-in ones.
3232

33-
The simplest approach to customization is wrapping an existing hook with your own function.
34-
A base hook can be obtained from a converter and be subjected to the very rich machinery of function composition in Python.
33+
The simplest approach to customization is writing a new hook from scratch.
34+
For example, we can write our own hook for the `int` class.
3535

3636
```python
37-
>>> from cattrs import get_structure_hook
37+
>>> def int_hook(value, type):
38+
... if not isinstance(value, int):
39+
... raise ValueError('not an int!')
40+
... return value
41+
```
3842

39-
>>> base_hook = get_structure_hook(Model)
43+
We can then register this hook to a converter and any other hook converting an `int` will use it.
4044

41-
>>> def my_hook(value, type):
45+
```python
46+
>>> from cattrs import Converter
47+
48+
>>> converter = Converter()
49+
>>> converter.register_structure_hook(int, int_hook)
50+
```
51+
52+
Another approach to customization is wrapping an existing hook with your own function.
53+
A base hook can be obtained from a converter and then be subjected to the very rich machinery of function composition that Python offers.
54+
55+
56+
```python
57+
>>> base_hook = converter.get_structure_hook(Model)
58+
59+
>>> def my_model_hook(value, type):
4260
... # Apply any preprocessing to the value.
4361
... result = base_hook(value, type)
44-
... # Apply any postprocessing to the value.
62+
... # Apply any postprocessing to the model.
4563
... return result
4664
```
4765

48-
This new hook can be used directly or registered to a converter (the original instance, or a different one).
49-
5066
(`cattrs.structure({}, Model)` is shorthand for `cattrs.get_structure_hook(Model)({}, Model)`.)
5167

52-
Another approach is to write a hook from scratch instead of wrapping an existing one.
53-
For example, we can write our own hook for the `int` class.
68+
This new hook can be used directly or registered to a converter (the original instance, or a different one):
5469

5570
```python
56-
>>> def my_int_hook(value, type):
57-
... if not isinstance(value, int):
58-
... raise ValueError('not an int!')
59-
... return value
71+
>>> converter.register_structure_hook(Model, my_model_hook)
6072
```
6173

62-
We can then register this hook to a converter, and any other hook converting an `int` will use it.
63-
Since this is an impactful change, we will switch to using a private converter.
64-
65-
```python
66-
>>> from cattrs import Converter
67-
68-
>>> c = Converter()
69-
70-
>>> c.register_structure_hook(int, my_int_hook)
71-
```
7274

73-
Now, if we ask our new converter for a `Model` hook, through the ✨magic of function composition✨ that hook will use our new `my_int_hook`.
75+
Now if we use this hook to structure a `Model`, through the ✨magic of function composition✨ that hook will use our old `int_hook`.
7476

7577
```python
76-
>>> base_hook = c.get_structure_hook(Model)
77-
>>> base_hook({"a": "1"}, Model)
78+
>>> converter.structure({"a": "1"}, Model)
7879
+ Exception Group Traceback (most recent call last):
7980
| File "...", line 22, in <module>
8081
| base_hook({"a": "1"}, Model)
@@ -95,7 +96,7 @@ More advanced structuring customizations are commonly called [](strategies.md).
9596

9697
## Global Converter
9798

98-
Global _cattrs_ functions, such as {meth}`cattrs.unstructure`, use a single {data}`global converter <cattrs.global_converter>`.
99+
Global _cattrs_ functions, such as {meth}`cattrs.structure`, use a single {data}`global converter <cattrs.global_converter>`.
99100
Changes done to this global converter, such as registering new structure and unstructure hooks, affect all code using the global functions.
100101

101102
The following functions implicitly use this global converter:

docs/cattrs.gen.rst

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
cattrs.gen package
22
==================
33

4+
.. automodule:: cattrs.gen
5+
:members:
6+
:undoc-members:
7+
:show-inheritance:
8+
49
Submodules
510
----------
611

@@ -11,11 +16,3 @@ cattrs.gen.typeddicts module
1116
:members:
1217
:undoc-members:
1318
:show-inheritance:
14-
15-
Module contents
16-
---------------
17-
18-
.. automodule:: cattrs.gen
19-
:members:
20-
:undoc-members:
21-
:show-inheritance:

docs/cattrs.preconf.rst

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
cattrs.preconf package
22
======================
33

4+
.. automodule:: cattrs.preconf
5+
:members:
6+
:undoc-members:
7+
:show-inheritance:
8+
49
Submodules
510
----------
611

@@ -67,11 +72,3 @@ cattrs.preconf.ujson module
6772
:members:
6873
:undoc-members:
6974
:show-inheritance:
70-
71-
Module contents
72-
---------------
73-
74-
.. automodule:: cattrs.preconf
75-
:members:
76-
:undoc-members:
77-
:show-inheritance:

docs/cattrs.rst

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
cattrs package
22
==============
33

4+
.. automodule:: cattrs
5+
:members:
6+
:undoc-members:
7+
:show-inheritance:
8+
49
Subpackages
510
-----------
611

@@ -14,14 +19,6 @@ Subpackages
1419
Submodules
1520
----------
1621

17-
cattrs.converters module
18-
------------------------
19-
20-
.. automodule:: cattrs.converters
21-
:members:
22-
:undoc-members:
23-
:show-inheritance:
24-
2522
cattrs.disambiguators module
2623
----------------------------
2724

@@ -61,11 +58,3 @@ cattrs.v module
6158
:members:
6259
:undoc-members:
6360
:show-inheritance:
64-
65-
Module contents
66-
---------------
67-
68-
.. automodule:: cattrs
69-
:members:
70-
:undoc-members:
71-
:show-inheritance:

docs/cattrs.strategies.rst

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
cattrs.strategies package
22
=========================
33

4-
Module contents
5-
---------------
6-
74
.. automodule:: cattrs.strategies
85
:members:
96
:undoc-members:

docs/conf.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
# -*- coding: utf-8 -*-
2-
#
31
# cattrs documentation build configuration file, created by
42
# sphinx-quickstart on Tue Jul 9 22:26:36 2013.
53
#
@@ -289,6 +287,7 @@
289287
"from typing import *;"
290288
"from enum import Enum, unique"
291289
)
290+
autodoc_member_order = "bysource"
292291
autodoc_typehints = "description"
293292
autosectionlabel_prefix_document = True
294293
copybutton_prompt_text = r">>> |\.\.\. "

docs/customizing.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,7 @@ Hook factories are registered using {meth}`Converter.register_unstructure_hook_f
6262

6363
Here's an example showing how to use hook factories to apply the `forbid_extra_keys` to all attrs classes:
6464

65-
```{doctest}
66-
65+
```python
6766
>>> from attrs import define, has
6867
>>> from cattrs.gen import make_dict_structure_fn
6968

@@ -135,7 +134,7 @@ So we apply the `omit_if_default` rule to the class, but not to the `dateTime` f
135134
>>> @define
136135
... class TestClass:
137136
... a: Optional[int] = None
138-
... b: dateTime = Factory(datetime.utcnow)
137+
... b: datetime = Factory(datetime.utcnow)
139138

140139
>>> c = cattrs.Converter()
141140
>>> hook = make_dict_unstructure_fn(TestClass, c, _cattrs_omit_if_default=True, b=override(omit_if_default=False))

0 commit comments

Comments
 (0)