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/typed dicts #364

Merged
merged 8 commits into from
May 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
2 changes: 2 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

- Introduce the `tagged_union` strategy. ([#318](https://github.com/python-attrs/cattrs/pull/318) [#317](https://github.com/python-attrs/cattrs/issues/317))
- Introduce the `cattrs.transform_error` helper function for formatting validation exceptions. ([258](https://github.com/python-attrs/cattrs/issues/258) [342](https://github.com/python-attrs/cattrs/pull/342))
- Add support for [`typing.TypedDict` and `typing_extensions.TypedDict`](https://peps.python.org/pep-0589/).
([#296](https://github.com/python-attrs/cattrs/issues/296) [#364](https://github.com/python-attrs/cattrs/pull/364))
- Add support for `typing.Final`.
([#340](https://github.com/python-attrs/cattrs/issues/340) [#349](https://github.com/python-attrs/cattrs/pull/349))
- Introduce `override.struct_hook` and `override.unstruct_hook`. Learn more [here](https://catt.rs/en/latest/customizing.html#struct-hook-and-unstruct-hook).
Expand Down
21 changes: 21 additions & 0 deletions docs/cattrs.gen.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
cattrs.gen package
==================

Submodules
----------

cattrs.gen.typeddicts module
----------------------------

.. automodule:: cattrs.gen.typeddicts
:members:
:undoc-members:
:show-inheritance:

Module contents
---------------

.. automodule:: cattrs.gen
:members:
:undoc-members:
:show-inheritance:
9 changes: 1 addition & 8 deletions docs/cattrs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Subpackages
.. toctree::
:maxdepth: 4

cattrs.gen
cattrs.preconf
cattrs.strategies

Expand Down Expand Up @@ -45,14 +46,6 @@ cattrs.errors module
:undoc-members:
:show-inheritance:

cattrs.gen module
-----------------

.. automodule:: cattrs.gen
:members:
:undoc-members:
:show-inheritance:

cattrs.v module
---------------

Expand Down
58 changes: 57 additions & 1 deletion docs/structuring.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,62 @@ and values can be converted.
{'1': None, '2': 2}
```

### Typed Dicts

[TypedDicts](https://peps.python.org/pep-0589/) can be produced from mapping objects, usually dictionaries.

```{doctest}
>>> from typing import TypedDict

>>> class MyTypedDict(TypedDict):
... a: int

>>> cattrs.structure({"a": "1"}, MyTypedDict)
{'a': 1}
```

Both [_total_ and _non-total_](https://peps.python.org/pep-0589/#totality) TypedDicts are supported, and inheritance between any combination works (except on 3.8 when `typing.TypedDict` is used, see below).
Generic TypedDicts work on Python 3.11 and later, since that is the first Python version that supports them in general.

[`typing.Required` and `typing.NotRequired`](https://peps.python.org/pep-0655/) are supported.

On Python 3.7, using `typing_extensions.TypedDict` is required since `typing.TypedDict` doesn't exist there.
On Python 3.8, using `typing_extensions.TypedDict` is recommended since `typing.TypedDict` doesn't support all necessary features, so certain combinations of subclassing, totality and `typing.Required` won't work.

[Similar to _attrs_ classes](customizing.md#using-cattrsgen-generators), structuring can be customized using {meth}`cattrs.gen.typeddicts.make_dict_structure_fn`.

```{doctest}
>>> from typing import TypedDict
>>> from cattrs import Converter
>>> from cattrs.gen import override
>>> from cattrs.gen.typeddicts import make_dict_structure_fn

>>> class MyTypedDict(TypedDict):
... a: int
... b: int

>>> c = Converter()
>>> c.register_structure_hook(
... MyTypedDict,
... make_dict_structure_fn(
... MyTypedDict,
... c,
... a=override(rename="a-with-dash")
... )
... )

>>> c.structure({"a-with-dash": 1, "b": 2}, MyTypedDict)
{'b': 2, 'a': 1}
```

```{seealso} [Unstructuring TypedDicts.](unstructuring.md#typed-dicts)

```

```{versionadded} 23.1.0

```

### Homogeneous and Heterogeneous Tuples

Homogeneous and heterogeneous tuples can be produced from iterable objects.
Expand Down Expand Up @@ -436,7 +492,7 @@ annotations when using Python 3.6+, or by passing the appropriate type to
... a: int

>>> attr.fields(A).a
Attribute(name='a', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({}), type=<class 'int'>, converter=None, kw_only=False, inherited=False, on_setattr=None)
Attribute(name='a', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({}), type=<class 'int'>, converter=None, kw_only=False, inherited=False, on_setattr=None, alias='a')
```

Type information, when provided, can be used for all attribute types, not only
Expand Down
60 changes: 59 additions & 1 deletion docs/unstructuring.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,64 @@ True
False
```

### Typed Dicts

[TypedDicts](https://peps.python.org/pep-0589/) unstructure into dictionaries, potentially unchanged (depending on the exact field types and registered hooks).

```{doctest}
>>> from typing import TypedDict
>>> from datetime import datetime, timezone
>>> from cattrs import Converter

>>> class MyTypedDict(TypedDict):
... a: datetime

>>> c = Converter()
>>> c.register_unstructure_hook(datetime, lambda d: d.timestamp())

>>> c.unstructure({"a": datetime(1970, 1, 1, tzinfo=timezone.utc)}, unstructure_as=MyTypedDict)
{'a': 0.0}
```

Generic TypedDicts work on Python 3.11 and later, since that is the first Python version that supports them in general.

On Python 3.7, using `typing_extensions.TypedDict` is required since `typing.TypedDict` doesn't exist there.
On Python 3.8, using `typing_extensions.TypedDict` is recommended since `typing.TypedDict` doesn't support all necessary features, so certain combinations of subclassing, totality and `typing.Required` won't work.

[Similar to _attrs_ classes](customizing.md#using-cattrsgen-generators), unstructuring can be customized using {meth}`cattrs.gen.typeddicts.make_dict_unstructure_fn`.

```{doctest}
>>> from typing import TypedDict
>>> from cattrs import Converter
>>> from cattrs.gen import override
>>> from cattrs.gen.typeddicts import make_dict_unstructure_fn

>>> class MyTypedDict(TypedDict):
... a: int
... b: int

>>> c = Converter()
>>> c.register_unstructure_hook(
... MyTypedDict,
... make_dict_unstructure_fn(
... MyTypedDict,
... c,
... a=override(omit=True)
... )
... )

>>> c.unstructure({"a": 1, "b": 2}, unstructure_as=MyTypedDict)
{'b': 2}
```

```{seealso} [Structuring TypedDicts.](structuring.md#typed-dicts)

```

```{versionadded} 23.1.0

```

## `pathlib.Path`

[`pathlib.Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path) objects are unstructured into their string value.
Expand All @@ -57,7 +115,7 @@ generic way. A common example is using a JSON library that doesn't support
sets, but expects lists and tuples instead.

Using ordinary unstructuring hooks for this is unwieldy due to the semantics of
{ref}`singledispatch <https://docs.python.org/3/library/functools.html#functools.singledispatch>`;
[singledispatch](https://docs.python.org/3/library/functools.html#functools.singledispatch);
in other words, you'd need to register hooks for all specific types of set you're using (`set[int]`, `set[float]`,
`set[str]`...), which is not useful.

Expand Down
Loading