バリデーション

cattrs にはバージョン 22.1.0 から詳細なバリデーションモードがあり、デフォルトで有効になっています。詳細バリデーションを実行すると、ストラクチャリングフックは若干遅くなりますが、より豊富で正確なエラーメッセージが生成されます。アンストラクチャリングフックは影響を受けません。

詳細なバリデーション

Added in version 22.1.0.

詳細バリデーションモードでは、ストラクチャリングエラーはグループ化され、cattrs.BaseValidationError としてまとめて発生します。これは PEP 654 ExceptionGroup です。ExceptionGroup は、他の例外のリストを含む特別な例外であり、それ自体が他の ExceptionGroup である可能性があります。本質的に、ExceptionGroup は例外のツリーです。

クラスをストラクチャリングする際、cattrs はフィールドごとに例外を収集し、cattrs.ClassValidationError として発生させます。これは BaseValidationError のサブクラスです。

シーケンスとマッピングをストラクチャリングする際、cattrs はキーまたはインデックスごとに例外を収集し、cattrs.IterableValidationError として発生させます。これは BaseValidationError のサブクラスです。

例外は、PEP 678 に従って、__notes__ 属性が設定され、内部例外ごとにフィールド、キー、またはインデックスが表示されます。

リストと辞書を含むクラスの簡単な例:

@define
class Class:
    a_list: list[int]
    a_dict: dict[str, int]

>>> structure({"a_list": ["a"], "a_dict": {"str": "a"}}, Class)
  + 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'
      +------------------------------------

例外をエラーメッセージに変換する

Added in version 23.1.0.

ExceptionGroup のスタックトレースは開発中に役立ちますが、バリデーションエラーのよりコンパクトな表現が必要になる場合があります。cattrs は、バリデーションエラーをエラーメッセージのリストに変換するヘルパー関数 cattrs.transform_error() を提供します。

前の段落の例では、次のエラーメッセージが生成されます:

>>> from cattrs import structure, transform_error

>>> try:
...     structure({"a_list": ["a"], "a_dict": {"str": "a"}}, Class)
... except Exception as exc:
...     print(transform_error(exc))
['invalid value for type, expected int @ $.a_list[0]', "invalid value for type, expected int @ $.a_dict['str']"]

少数の組み込み例外は、自動的にエラーメッセージに変換されます。これは、個々の ExceptionGroup 以外の例外をエラーメッセージに変換するために使用できる関数を cattrs.transform_error() に提供することで、さらにカスタマイズできます。便利なパターンは、デフォルトの cattrs.v.format_exception() 関数をラップすることです。

>>> from cattrs.v import format_exception

>>> def my_exception_formatter(exc: BaseException, type) -> str:
...     if isinstance(exc, MyInterestingException):
...         return "My error message"
...     return format_exception(exc, type)

>>> try:
...     structure(..., Class)
... except Exception as exc:
...     print(transform_error(exc, format_exception=my_exception_formatter))

さらにカスタマイズが必要な場合は、cattrs.transform_error() をコードベースにコピーして、必要に応じて調整できます。

詳細でないバリデーション

詳細でないバリデーションは、いずれかのコンバーターを detailed_validation=False で初期化することで有効にできます。このモードでは、アン/ストラクチャリング中のエラーは、発生するとすぐに直接バブルアップします。