Skip to content
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
5 changes: 5 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

## 24.1.0 (UNRELEASED)

- **Potentially breaking**: Unstructuring hooks for `typing.Any` are consistent now: values are unstructured using their runtime type.
Previously this behavior was underspecified and inconsistent, but followed this rule in the majority of cases.
([#473](https://github.com/python-attrs/cattrs/pull/473))
- 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.
Expand All @@ -26,6 +29,8 @@
- 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 documentation has been significantly reworked.
([#473](https://github.com/python-attrs/cattrs/pull/473))
- The docs now use the Inter font.

## 23.2.3 (2023-11-30)
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# cattrs

<p>
<em>Great software needs great data structures.</em>
</p>

<a href="https://pypi.python.org/pypi/cattrs"><img src="https://img.shields.io/pypi/v/cattrs.svg"/></a>
<a href="https://github.com/python-attrs/cattrs/actions?workflow=CI"><img src="https://github.com/python-attrs/cattrs/workflows/CI/badge.svg"/></a>
<a href="https://catt.rs/en/latest/?badge=latest"><img src="https://readthedocs.org/projects/cattrs/badge/?version=latest" alt="Documentation Status"/></a>
Expand Down Expand Up @@ -103,7 +107,7 @@ When you're done, `unstructure` the data to its unstructured form and pass it al
Use [attrs type metadata](http://attrs.readthedocs.io/en/stable/examples.html#types) to add type metadata to attributes, so _cattrs_ will know how to structure and destructure them.

- Free software: MIT license
- Documentation: https://catt.rs
- Documentation: [https://catt.rs](https://catt.rs)
- Python versions supported: 3.8 and up. (Older Python versions are supported by older versions; see the changelog.)

## Features
Expand Down
3 changes: 2 additions & 1 deletion docs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,9 @@ pseudoxml:
@echo
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."

.PHONY: apidoc
apidoc:
pdm run sphinx-apidoc -o . ../src/cattrs/ -f
pdm run sphinx-apidoc -o . ../src/cattrs/ '../**/converters.py' -f -M

## htmlview to open the index page built by the html target in your browser
.PHONY: htmlview
Expand Down
67 changes: 34 additions & 33 deletions docs/basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
```

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.
A global converter is provided for convenience as {data}`cattrs.global_converter` but more complex customizations should be performed on private instances, any number of which can be made.


## Converters
## Converters and Hooks

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_.
The core functionality of a converter is structuring and unstructuring data by composing [provided](defaulthooks.md) 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.
The two main methods, {meth}`structure <cattrs.BaseConverter.structure>` and {meth}`unstructure <cattrs.BaseConverter.unstructure>`, are used to convert between _structured_ and _unstructured_ data.

```python
>>> from cattrs import structure, unstructure
Expand All @@ -28,53 +28,54 @@ The two main methods are {meth}`structure <cattrs.BaseConverter.structure>` and
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.
_cattrs_ comes with a rich library of un/structuring hooks by default but it excels at composing custom hooks 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 machinery of function composition in Python.
The simplest approach to customization is writing a new hook from scratch.
For example, we can write our own hook for the `int` class.

```python
>>> from cattrs import get_structure_hook
>>> def int_hook(value, type):
... if not isinstance(value, int):
... raise ValueError('not an int!')
... return value
```

>>> base_hook = get_structure_hook(Model)
We can then register this hook to a converter and any other hook converting an `int` will use it.

>>> def my_hook(value, type):
```python
>>> from cattrs import Converter

>>> converter = Converter()
>>> converter.register_structure_hook(int, int_hook)
```

Another approach to customization is wrapping an existing hook with your own function.
A base hook can be obtained from a converter and then be subjected to the very rich machinery of function composition that Python offers.


```python
>>> base_hook = converter.get_structure_hook(Model)

>>> def my_model_hook(value, type):
... # Apply any preprocessing to the value.
... result = base_hook(value, type)
... # Apply any postprocessing to the value.
... # Apply any postprocessing to the model.
... 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.
This new hook can be used directly or registered to a converter (the original instance, or a different one):

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

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`.
Now if we use this hook to structure a `Model`, through the ✨magic of function composition✨ that hook will use our old `int_hook`.

```python
>>> base_hook = c.get_structure_hook(Model)
>>> base_hook({"a": "1"}, Model)
>>> converter.structure({"a": "1"}, Model)
+ Exception Group Traceback (most recent call last):
| File "...", line 22, in <module>
| base_hook({"a": "1"}, Model)
Expand All @@ -95,7 +96,7 @@ 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>`.
Global _cattrs_ functions, such as {meth}`cattrs.structure`, 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:
Expand Down
13 changes: 5 additions & 8 deletions docs/cattrs.gen.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
cattrs.gen package
==================

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

Submodules
----------

Expand All @@ -11,11 +16,3 @@ cattrs.gen.typeddicts module
:members:
:undoc-members:
:show-inheritance:

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

.. automodule:: cattrs.gen
:members:
:undoc-members:
:show-inheritance:
13 changes: 5 additions & 8 deletions docs/cattrs.preconf.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
cattrs.preconf package
======================

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

Submodules
----------

Expand Down Expand Up @@ -67,11 +72,3 @@ cattrs.preconf.ujson module
:members:
:undoc-members:
:show-inheritance:

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

.. automodule:: cattrs.preconf
:members:
:undoc-members:
:show-inheritance:
21 changes: 5 additions & 16 deletions docs/cattrs.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
cattrs package
==============

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

Subpackages
-----------

Expand All @@ -14,14 +19,6 @@ Subpackages
Submodules
----------

cattrs.converters module
------------------------

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

cattrs.disambiguators module
----------------------------

Expand Down Expand Up @@ -61,11 +58,3 @@ cattrs.v module
:members:
:undoc-members:
:show-inheritance:

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

.. automodule:: cattrs
:members:
:undoc-members:
:show-inheritance:
3 changes: 0 additions & 3 deletions docs/cattrs.strategies.rst
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
cattrs.strategies package
=========================

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

.. automodule:: cattrs.strategies
:members:
:undoc-members:
Expand Down
3 changes: 1 addition & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
#
# cattrs documentation build configuration file, created by
# sphinx-quickstart on Tue Jul 9 22:26:36 2013.
#
Expand Down Expand Up @@ -289,6 +287,7 @@
"from typing import *;"
"from enum import Enum, unique"
)
autodoc_member_order = "bysource"
autodoc_typehints = "description"
autosectionlabel_prefix_document = True
copybutton_prompt_text = r">>> |\.\.\. "
Expand Down
5 changes: 2 additions & 3 deletions docs/customizing.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,7 @@ Hook factories are registered using {meth}`Converter.register_unstructure_hook_f

Here's an example showing how to use hook factories to apply the `forbid_extra_keys` to all attrs classes:

```{doctest}

```python
>>> from attrs import define, has
>>> from cattrs.gen import make_dict_structure_fn

Expand Down Expand Up @@ -135,7 +134,7 @@ So we apply the `omit_if_default` rule to the class, but not to the `dateTime` f
>>> @define
... class TestClass:
... a: Optional[int] = None
... b: dateTime = Factory(datetime.utcnow)
... b: datetime = Factory(datetime.utcnow)

>>> c = cattrs.Converter()
>>> hook = make_dict_unstructure_fn(TestClass, c, _cattrs_omit_if_default=True, b=override(omit_if_default=False))
Expand Down
Loading