Skip to content

Commit

Permalink
Display tweak and docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Tinche committed Mar 3, 2022
1 parent edd3d0f commit 3a5c1f6
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 1 deletion.
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Contents:
converters
structuring
unstructuring
validation
preconf
customizing
unions
Expand Down
74 changes: 74 additions & 0 deletions docs/validation.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
==========
Validation
==========

`cattrs` has a detailed validation mode since version 2022.1.0, and this mode is enabled by default.
When running under detailed validation, the un/structuring hooks are slightly slower but produce more precise and exhaustive error messages.

Detailed validation
-------------------
In detailed validation mode, any un/structuring errors will be grouped and raised together as a ``cattrs.BaseValidationError``, which is a `PEP 654 ExceptionGroup`_.
ExceptionGroups are special exceptions which contain lists of other exceptions, which may themselves be other ExceptionGroups.
In essence, ExceptionGroups are trees of exceptions.

When un/structuring a class, `cattrs` will gather any exceptions on a field-by-field basis and raise them as a ``cattrs.ClassValidationError``, which is a subclass of ``BaseValidationError``.
When structuring sequences and mappings, `cattrs` will gather any exceptions on a key- or index-basis and raise them as a ``cattrs.IterableValidationError``, which is a subclass of ``BaseValidationError``.

The exceptions will also have their ``__note__`` attributes set, as per `PEP 678`_, showing the field, key or index for each inner exception.

A simple example involving a class containing a list and a dictionary:

.. code-block:: python
@define
class Class:
a_list: list[int]
a_dict: dict[str, int]
>>> structure({"a_list": ["a"], "a_dict": {"str": "a"}})
+ Exception Group Traceback (most recent call last):
| File "<stdin>", line 1, in <module>
| File "/Users/tintvrtkovic/pg/cattrs/src/cattr/converters.py", line 276, in structure
| return self._structure_func.dispatch(cl)(obj, cl)
| File "<cattrs generated structure __main__.Class>", line 14, in structure_Class
| if errors: raise __c_cve('While structuring Class', errors, __cl)
| cattrs.errors.ClassValidationError: While structuring Class
+-+---------------- 1 ----------------
| Exception Group Traceback (most recent call last):
| File "<cattrs generated structure __main__.Class>", line 5, in structure_Class
| res['a_list'] = __c_structure_a_list(o['a_list'], __c_type_a_list)
| File "/Users/tintvrtkovic/pg/cattrs/src/cattr/converters.py", line 457, in _structure_list
| raise IterableValidationError(
| cattrs.errors.IterableValidationError: While structuring list[int]
| Structuring class Class @ attribute a_list
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "/Users/tintvrtkovic/pg/cattrs/src/cattr/converters.py", line 450, in _structure_list
| res.append(handler(e, elem_type))
| File "/Users/tintvrtkovic/pg/cattrs/src/cattr/converters.py", line 375, in _structure_call
| return cl(obj)
| ValueError: invalid literal for int() with base 10: 'a'
| Structuring list[int] @ index 0
+------------------------------------
+---------------- 2 ----------------
| Exception Group Traceback (most recent call last):
| File "<cattrs generated structure __main__.Class>", line 10, in structure_Class
| res['a_dict'] = __c_structure_a_dict(o['a_dict'], __c_type_a_dict)
| File "", line 17, in structure_mapping
| cattrs.errors.IterableValidationError: While structuring dict
| Structuring class Class @ attribute a_dict
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "", line 5, in structure_mapping
| ValueError: invalid literal for int() with base 10: 'a'
| Structuring mapping value @ key 'str'
+------------------------------------
.. _`PEP 654 ExceptionGroup`: https://www.python.org/dev/peps/pep-0654/
.. _`PEP 678`: https://www.python.org/dev/peps/pep-0678/
Non-detailed validation
-----------------------
Non-detailed validation can be enabled by initializing any of the converters with ``detailed_validation=False``.
In this mode, any errors during un/structuring will bubble up directly as soon as they happen
2 changes: 1 addition & 1 deletion src/cattrs/gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -679,7 +679,7 @@ def make_mapping_structure_fn(
lines.append(" errors.append(e)")
lines.append(" if errors:")
lines.append(
f" raise IterableValidationError('While structuring {structure_to.__name__}', errors, __cattr_mapping_cl)"
f" raise IterableValidationError('While structuring {cl!r}', errors, __cattr_mapping_cl)"
)
else:
lines.append(f" res = {{{k_s}: {v_s} for k, v in mapping.items()}}")
Expand Down

0 comments on commit 3a5c1f6

Please sign in to comment.