Skip to content

Commit

Permalink
Docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Tinche committed Jul 8, 2023
1 parent 7b7d250 commit fb8f71b
Show file tree
Hide file tree
Showing 9 changed files with 192 additions and 38 deletions.
2 changes: 1 addition & 1 deletion docs/Makefile
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
68 changes: 67 additions & 1 deletion pdm.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ docs = [
"furo>=2023.3.27",
"sphinx-copybutton>=0.5.2",
"myst-parser>=1.0.0",
"pendulum>=2.1.2",
]

[tool.pytest.ini_options]
Expand Down

0 comments on commit fb8f71b

Please sign in to comment.