Skip to content

Commit f7b5924

Browse files
authored
Tin/attrs-alias (#391)
* alias work * Docs * Bump deps * Appease ruff
1 parent 93d83f8 commit f7b5924

File tree

13 files changed

+548
-356
lines changed

13 files changed

+548
-356
lines changed

HISTORY.md

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

55
- Fix `format_exception` parameter working for recursive calls to `transform_error`
66
([#389](https://github.com/python-attrs/cattrs/issues/389)
7+
- [_attrs_ aliases](https://www.attrs.org/en/stable/init.html#private-attributes-and-aliases) are now supported, although aliased fields still map to their attribute name instead of their alias by default when un/structuring.
8+
([#322](https://github.com/python-attrs/cattrs/issues/322) [#391](https://github.com/python-attrs/cattrs/pull/391))
79
- Use [PDM](https://pdm.fming.dev/latest/) instead of Poetry.
810
- _cattrs_ is now linted with [Ruff](https://beta.ruff.rs/docs/).
911
- Fix TypedDicts with periods in their field names.

docs/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,4 +177,4 @@ pseudoxml:
177177
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
178178

179179
apidoc:
180-
sphinx-apidoc -o . ../src/cattrs/ -f
180+
sphinx-apidoc -o . ../src/cattrs/ -f

docs/customizing.md

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,27 @@
1-
# Customizing class un/structuring
1+
# Customizing Class Un/structuring
22

3-
This section deals with customizing the unstructuring and structuring processes in `cattrs`.
3+
This section deals with customizing the unstructuring and structuring processes in _cattrs_.
44

5-
## Using `cattr.Converter`
5+
## Using `cattrs.Converter`
66

7-
The default `Converter`, upon first encountering an `attrs` class, will use
8-
the generation functions mentioned here to generate the specialized hooks for it,
9-
register the hooks and use them.
7+
The default {class}`Converter <cattrs.Converter>`, upon first encountering an _attrs_ class, will use the generation functions mentioned here to generate the specialized hooks for it, register the hooks and use them.
108

119
## Manual un/structuring hooks
1210

1311
You can write your own structuring and unstructuring functions and register
14-
them for types using {meth}`Converter.register_structure_hook <cattrs.BaseConverter.register_structure_hook>` and
15-
{meth}`Converter.register_unstructure_hook <cattrs.BaseConverter.register_unstructure_hook>`. This approach is the most
12+
them for types using {meth}`Converter.register_structure_hook() <cattrs.BaseConverter.register_structure_hook>` and
13+
{meth}`Converter.register_unstructure_hook() <cattrs.BaseConverter.register_unstructure_hook>`. This approach is the most
1614
flexible but also requires the most amount of boilerplate.
1715

1816
## Using `cattrs.gen` generators
1917

20-
`cattrs` includes a module, {mod}`cattrs.gen`, which allows for generating and
21-
compiling specialized functions for unstructuring `attrs` classes.
18+
_cattrs_ includes a module, {mod}`cattrs.gen`, which allows for generating and compiling specialized functions for unstructuring _attrs_ classes.
2219

23-
One reason for generating these functions in advance is that they can bypass
24-
a lot of `cattrs` machinery and be significantly faster than normal `cattrs`.
20+
One reason for generating these functions in advance is that they can bypass a lot of _cattrs_ machinery and be significantly faster than normal _cattrs_.
2521

2622
Another reason is that it's possible to override behavior on a per-attribute basis.
2723

28-
Currently, the overrides only support generating dictionary un/structuring functions
29-
(as opposed to tuples), and support `omit_if_default`, `forbid_extra_keys`,
30-
`rename` and `omit`.
24+
Currently, the overrides only support generating dictionary un/structuring functions (as opposed to tuples), and support `omit_if_default`, `forbid_extra_keys`, `rename` and `omit`.
3125

3226
### `omit_if_default`
3327

@@ -82,13 +76,10 @@ This override has no effect when generating structuring functions.
8276

8377
### `forbid_extra_keys`
8478

85-
By default `cattrs` is lenient in accepting unstructured input. If extra
86-
keys are present in a dictionary, they will be ignored when generating a
87-
structured object. Sometimes it may be desirable to enforce a stricter
88-
contract, and to raise an error when unknown keys are present - in particular
89-
when fields have default values this may help with catching typos.
90-
`forbid_extra_keys` can also be enabled (or disabled) on a per-class basis when
91-
creating structure hooks with `make_dict_structure_fn`.
79+
By default _cattrs_ is lenient in accepting unstructured input.
80+
If extra keys are present in a dictionary, they will be ignored when generating a structured object.
81+
Sometimes it may be desirable to enforce a stricter contract, and to raise an error when unknown keys are present - in particular when fields have default values this may help with catching typos.
82+
`forbid_extra_keys` can also be enabled (or disabled) on a per-class basis when creating structure hooks with {py:func}`make_dict_structure_fn() <cattrs.gen.make_dict_structure_fn>`.
9283

9384
```{doctest}
9485
:options: +SKIP
@@ -110,8 +101,7 @@ ForbiddenExtraKeyError: Extra fields in constructor for TestClass: nummber
110101
TestClass(number=1)
111102
```
112103

113-
This behavior can only be applied to classes or to the default for the
114-
`Converter`, and has no effect when generating unstructuring functions.
104+
This behavior can only be applied to classes or to the default for the {class}`Converter <cattrs.Converter>`, and has no effect when generating unstructuring functions.
115105

116106
### `rename`
117107

@@ -183,3 +173,28 @@ This process can be overriden by passing in the desired un/structure manually.
183173
>>> c.structure({"an_int": 1}, ExampleClass)
184174
ExampleClass(an_int=2)
185175
```
176+
177+
### `use_alias`
178+
179+
By default, fields are un/structured to and from dictionary keys exactly matching the field names.
180+
_attrs_ classes support field aliases, which override the `__init__` parameter name for a given field.
181+
By generating your un/structure function with `_cattrs_use_alias=True`, _cattrs_ will use the field alias instead of the field name as the un/structured dictionary key.
182+
183+
```{doctest}
184+
185+
>>> from cattrs.gen import make_dict_structure_fn
186+
>>>
187+
>>> @define
188+
... class AliasClass:
189+
... number: int = field(default=1, alias="count")
190+
>>>
191+
>>> c = cattrs.Converter()
192+
>>> hook = make_dict_structure_fn(AliasClass, c, _cattrs_use_alias=True)
193+
>>> c.register_structure_hook(AliasClass, hook)
194+
>>> c.structure({"count": 2}, AliasClass)
195+
AliasClass(number=2)
196+
```
197+
198+
```{versionadded} 23.2.0
199+
200+
```

docs/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ converters
99
usage
1010
structuring
1111
unstructuring
12+
customizing
1213
strategies
1314
validation
1415
preconf
15-
customizing
1616
unions
1717
benchmarking
1818
contributing

docs/strategies.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ The converter is now ready to start structuring Apple notifications.
120120

121121
```
122122

123+
```{versionadded} 23.1.0
124+
125+
```
126+
123127
## Include Subclasses Strategy
124128

125129
_Found at {py:func}`cattrs.strategies.include_subclasses`._
@@ -250,3 +254,7 @@ Here is an example involving both customizations:
250254
>>> converter.structure({'a': 1, 'c': 'foo'}, Parent)
251255
Child(a=1, b='foo')
252256
```
257+
258+
```{versionadded} 23.1.0
259+
260+
```

docs/structuring.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ so this operation can be used to copy an iterable into a deque.
159159
If you want to convert into bounded `deque`, registering a custom structuring hook is a good approach.
160160

161161
```{doctest}
162+
163+
>>> from collections import deque
162164
>>> cattrs.structure((1, 2, 3), deque[int])
163165
deque([1, 2, 3])
164166
```
@@ -202,7 +204,7 @@ These generic types are composable with all other converters.
202204
```{doctest}
203205

204206
>>> cattrs.structure([[1, 2], [3, 4]], set[frozenset[str]])
205-
{frozenset({'1', '2'}), frozenset({'4', '3'})}
207+
{frozenset({'2', '1'}), frozenset({'4', '3'})}
206208
```
207209

208210
### Dictionaries

0 commit comments

Comments
 (0)