Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tin/attrs-alias #391

Merged
merged 4 commits into from Jul 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions HISTORY.md
Expand Up @@ -4,6 +4,8 @@

- Fix `format_exception` parameter working for recursive calls to `transform_error`
([#389](https://github.com/python-attrs/cattrs/issues/389)
- [_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.
([#322](https://github.com/python-attrs/cattrs/issues/322) [#391](https://github.com/python-attrs/cattrs/pull/391))
- Use [PDM](https://pdm.fming.dev/latest/) instead of Poetry.
- _cattrs_ is now linted with [Ruff](https://beta.ruff.rs/docs/).
- Fix TypedDicts with periods in their field names.
Expand Down
2 changes: 1 addition & 1 deletion docs/Makefile
Expand Up @@ -177,4 +177,4 @@ pseudoxml:
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."

apidoc:
sphinx-apidoc -o . ../src/cattrs/ -f
sphinx-apidoc -o . ../src/cattrs/ -f
63 changes: 39 additions & 24 deletions docs/customizing.md
@@ -1,33 +1,27 @@
# Customizing class un/structuring
# Customizing Class Un/structuring

This section deals with customizing the unstructuring and structuring processes in `cattrs`.
This section deals with customizing the unstructuring and structuring processes in _cattrs_.

## Using `cattr.Converter`
## Using `cattrs.Converter`

The default `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.
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.

## Manual un/structuring hooks

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

## Using `cattrs.gen` generators

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

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`.
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_.

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

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`.
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`.

### `omit_if_default`

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

### `forbid_extra_keys`

By default `cattrs` is lenient in accepting unstructured input. If extra
keys are present in a dictionary, they will be ignored when generating a
structured object. 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.
`forbid_extra_keys` can also be enabled (or disabled) on a per-class basis when
creating structure hooks with `make_dict_structure_fn`.
By default _cattrs_ is lenient in accepting unstructured input.
If extra keys are present in a dictionary, they will be ignored when generating a structured object.
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.
`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>`.

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

This behavior can only be applied to classes or to the default for the
`Converter`, and has no effect when generating unstructuring functions.
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.

### `rename`

Expand Down Expand Up @@ -183,3 +173,28 @@ This process can be overriden by passing in the desired un/structure manually.
>>> c.structure({"an_int": 1}, ExampleClass)
ExampleClass(an_int=2)
```

### `use_alias`

By default, fields are un/structured to and from dictionary keys exactly matching the field names.
_attrs_ classes support field aliases, which override the `__init__` parameter name for a given field.
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.

```{doctest}

>>> from cattrs.gen import make_dict_structure_fn
>>>
>>> @define
... class AliasClass:
... number: int = field(default=1, alias="count")
>>>
>>> c = cattrs.Converter()
>>> hook = make_dict_structure_fn(AliasClass, c, _cattrs_use_alias=True)
>>> c.register_structure_hook(AliasClass, hook)
>>> c.structure({"count": 2}, AliasClass)
AliasClass(number=2)
```

```{versionadded} 23.2.0

```
2 changes: 1 addition & 1 deletion docs/index.md
Expand Up @@ -9,10 +9,10 @@ converters
usage
structuring
unstructuring
customizing
strategies
validation
preconf
customizing
unions
benchmarking
contributing
Expand Down
8 changes: 8 additions & 0 deletions docs/strategies.md
Expand Up @@ -120,6 +120,10 @@ The converter is now ready to start structuring Apple notifications.

```

```{versionadded} 23.1.0

```

## Include Subclasses Strategy

_Found at {py:func}`cattrs.strategies.include_subclasses`._
Expand Down Expand Up @@ -250,3 +254,7 @@ Here is an example involving both customizations:
>>> converter.structure({'a': 1, 'c': 'foo'}, Parent)
Child(a=1, b='foo')
```

```{versionadded} 23.1.0

```
4 changes: 3 additions & 1 deletion docs/structuring.md
Expand Up @@ -159,6 +159,8 @@ so this operation can be used to copy an iterable into a deque.
If you want to convert into bounded `deque`, registering a custom structuring hook is a good approach.

```{doctest}

>>> from collections import deque
>>> cattrs.structure((1, 2, 3), deque[int])
deque([1, 2, 3])
```
Expand Down Expand Up @@ -202,7 +204,7 @@ These generic types are composable with all other converters.
```{doctest}

>>> cattrs.structure([[1, 2], [3, 4]], set[frozenset[str]])
{frozenset({'1', '2'}), frozenset({'4', '3'})}
{frozenset({'2', '1'}), frozenset({'4', '3'})}
```

### Dictionaries
Expand Down