|
| 1 | +========== |
| 2 | +Validation |
| 3 | +========== |
| 4 | + |
| 5 | +`cattrs` has a detailed validation mode since version 2022.1.0, and this mode is enabled by default. |
| 6 | +When running under detailed validation, the un/structuring hooks are slightly slower but produce more precise and exhaustive error messages. |
| 7 | + |
| 8 | +Detailed validation |
| 9 | +------------------- |
| 10 | +In detailed validation mode, any un/structuring errors will be grouped and raised together as a ``cattrs.BaseValidationError``, which is a `PEP 654 ExceptionGroup`_. |
| 11 | +ExceptionGroups are special exceptions which contain lists of other exceptions, which may themselves be other ExceptionGroups. |
| 12 | +In essence, ExceptionGroups are trees of exceptions. |
| 13 | + |
| 14 | +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``. |
| 15 | +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``. |
| 16 | + |
| 17 | +The exceptions will also have their ``__note__`` attributes set, as per `PEP 678`_, showing the field, key or index for each inner exception. |
| 18 | + |
| 19 | +A simple example involving a class containing a list and a dictionary: |
| 20 | + |
| 21 | +.. code-block:: python |
| 22 | +
|
| 23 | + @define |
| 24 | + class Class: |
| 25 | + a_list: list[int] |
| 26 | + a_dict: dict[str, int] |
| 27 | +
|
| 28 | + >>> structure({"a_list": ["a"], "a_dict": {"str": "a"}}) |
| 29 | + + Exception Group Traceback (most recent call last): |
| 30 | + | File "<stdin>", line 1, in <module> |
| 31 | + | File "/Users/tintvrtkovic/pg/cattrs/src/cattr/converters.py", line 276, in structure |
| 32 | + | return self._structure_func.dispatch(cl)(obj, cl) |
| 33 | + | File "<cattrs generated structure __main__.Class>", line 14, in structure_Class |
| 34 | + | if errors: raise __c_cve('While structuring Class', errors, __cl) |
| 35 | + | cattrs.errors.ClassValidationError: While structuring Class |
| 36 | + +-+---------------- 1 ---------------- |
| 37 | + | Exception Group Traceback (most recent call last): |
| 38 | + | File "<cattrs generated structure __main__.Class>", line 5, in structure_Class |
| 39 | + | res['a_list'] = __c_structure_a_list(o['a_list'], __c_type_a_list) |
| 40 | + | File "/Users/tintvrtkovic/pg/cattrs/src/cattr/converters.py", line 457, in _structure_list |
| 41 | + | raise IterableValidationError( |
| 42 | + | cattrs.errors.IterableValidationError: While structuring list[int] |
| 43 | + | Structuring class Class @ attribute a_list |
| 44 | + +-+---------------- 1 ---------------- |
| 45 | + | Traceback (most recent call last): |
| 46 | + | File "/Users/tintvrtkovic/pg/cattrs/src/cattr/converters.py", line 450, in _structure_list |
| 47 | + | res.append(handler(e, elem_type)) |
| 48 | + | File "/Users/tintvrtkovic/pg/cattrs/src/cattr/converters.py", line 375, in _structure_call |
| 49 | + | return cl(obj) |
| 50 | + | ValueError: invalid literal for int() with base 10: 'a' |
| 51 | + | Structuring list[int] @ index 0 |
| 52 | + +------------------------------------ |
| 53 | + +---------------- 2 ---------------- |
| 54 | + | Exception Group Traceback (most recent call last): |
| 55 | + | File "<cattrs generated structure __main__.Class>", line 10, in structure_Class |
| 56 | + | res['a_dict'] = __c_structure_a_dict(o['a_dict'], __c_type_a_dict) |
| 57 | + | File "", line 17, in structure_mapping |
| 58 | + | cattrs.errors.IterableValidationError: While structuring dict |
| 59 | + | Structuring class Class @ attribute a_dict |
| 60 | + +-+---------------- 1 ---------------- |
| 61 | + | Traceback (most recent call last): |
| 62 | + | File "", line 5, in structure_mapping |
| 63 | + | ValueError: invalid literal for int() with base 10: 'a' |
| 64 | + | Structuring mapping value @ key 'str' |
| 65 | + +------------------------------------ |
| 66 | +
|
| 67 | +.. _`PEP 654 ExceptionGroup`: https://www.python.org/dev/peps/pep-0654/ |
| 68 | +.. _`PEP 678`: https://www.python.org/dev/peps/pep-0678/ |
| 69 | +
|
| 70 | +Non-detailed validation |
| 71 | +----------------------- |
| 72 | +
|
| 73 | +Non-detailed validation can be enabled by initializing any of the converters with ``detailed_validation=False``. |
| 74 | +In this mode, any errors during un/structuring will bubble up directly as soon as they happen |
0 commit comments