Skip to content

Tin/override-metadata #472

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

Merged
merged 8 commits into from
Dec 22, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,18 @@

## 24.1.0 (UNRELEASED)

- Introduce {meth}`BaseConverter.get_structure_hook` and {meth}`BaseConverter.get_unstructure_hook` methods.
([#432](https://github.com/python-attrs/cattrs/issues/432) [#472](https://github.com/python-attrs/cattrs/pull/472))
- The default union handler now properly takes renamed fields into account.
([#472](https://github.com/python-attrs/cattrs/pull/472))
- Add support for [PEP 695](https://peps.python.org/pep-0695/) type aliases.
([#452](https://github.com/python-attrs/cattrs/pull/452))
- The `include_subclasses` strategy now fetches the member hooks from the converter (making use of converter defaults) if overrides are not provided, instead of generating new hooks with no overrides.
([#429](https://github.com/python-attrs/cattrs/issues/429) [#472](https://github.com/python-attrs/cattrs/pull/472))
- The {class}`orjson preconf converter <cattrs.preconf.orjson.OrjsonConverter>` now passes through dates and datetimes to orjson while unstructuring, greatly improving speed.
([#463](https://github.com/python-attrs/cattrs/pull/463))
- `cattrs.gen` generators now attach metadata to the generated functions, making them introspectable.
([#472](https://github.com/python-attrs/cattrs/pull/472))
- More robust support for `Annotated` and `NotRequired` in TypedDicts.
([#450](https://github.com/python-attrs/cattrs/pull/450))
- `typing_extensions.Literal` is now automatically structured, just like `typing.Literal`.
Expand All @@ -16,6 +24,8 @@
([#452](https://github.com/python-attrs/cattrs/pull/452))
- Imports are now sorted using Ruff.
- Tests are run with the pytest-xdist plugin by default.
- Rework the introductory parts of the documentation, introducing the Basics section.
([#472](https://github.com/python-attrs/cattrs/pull/472))
- The docs now use the Inter font.

## 23.2.3 (2023-11-30)
Expand Down
112 changes: 112 additions & 0 deletions docs/basics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# The Basics
```{currentmodule} cattrs
```

All _cattrs_ functionality is exposed through a {class}`cattrs.Converter` object.
A global converter is provided for convenience as {data}`cattrs.global_converter` but more complex customizations should be performed on private instances.


## Converters

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

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

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.

```python
>>> from cattrs import structure, unstructure
>>> from attrs import define

>>> @define
... class Model:
... a: int

>>> unstructure(Model(1))
{"a": 1}
>>> structure({"a": 1}, Model)
Model(a=1)
```

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

The simplest approach to customization is wrapping an existing hook with your own function.
A base hook can be obtained from a converter and be subjected to the very rich mechanisms of Python function composition.

```python
>>> from cattrs import get_structure_hook

>>> base_hook = get_structure_hook(Model)

>>> def my_hook(value, type):
... # Apply any preprocessing to the value.
... result = base_hook(value, type)
... # Apply any postprocessing to the value.
... return result
```

This new hook can be used directly or registered to a converter (the original instance, or a different one).

(`cattrs.structure({}, Model)` is shorthand for `cattrs.get_structure_hook(Model)({}, Model)`.)

Another approach is to write a hook from scratch instead of wrapping an existing one.
For example, we can write our own hook for the `int` class.

```python
>>> def my_int_hook(value, type):
... if not isinstance(value, int):
... raise ValueError('not an int!')
... return value
```

We can then register this hook to a converter, and any other hook converting an `int` will use it.
Since this is an impactful change, we will switch to using a private converter.

```python
>>> from cattrs import Converter

>>> c = Converter()

>>> c.register_structure_hook(int, my_int_hook)
```

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

```python
>>> base_hook = c.get_structure_hook(Model)
>>> base_hook({"a": "1"}, Model)
+ Exception Group Traceback (most recent call last):
| File "...", line 22, in <module>
| base_hook({"a": "1"}, Model)
| File "<cattrs generated structure __main__.Model>", line 9, in structure_Model
| cattrs.errors.ClassValidationError: While structuring Model (1 sub-exception)
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "<cattrs generated structure __main__.Model>", line 5, in structure_Model
| File "...", line 15, in my_int_hook
| raise ValueError("not an int!")
| ValueError: not an int!
| Structuring class Model @ attribute a
+------------------------------------
```

To continue reading about customizing _cattrs_, see [](customizing.md).
More advanced structuring customizations are commonly called [](strategies.md).

## Global Converter

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

The following functions implicitly use this global converter:

- {meth}`cattrs.structure`
- {meth}`cattrs.unstructure`
- {meth}`cattrs.get_structure_hook`
- {meth}`cattrs.get_unstructure_hook`
- {meth}`cattrs.structure_attrs_fromtuple`
- {meth}`cattrs.structure_attrs_fromdict`

Changes made to the global converter will affect the behavior of these functions.

Larger applications are strongly encouraged to create and customize different, private instances of {class}`cattrs.Converter`.
95 changes: 0 additions & 95 deletions docs/converters.md

This file was deleted.

Loading