cattrs: Composable Attrs/Dataclass Structuring

raw JSON →
26.1.0 verified Tue May 12 auth: no python install: verified quickstart: verified

cattrs is a Python library (version 26.1.0) that provides composable tools for converting between unstructured Python data (like dictionaries) and structured data (like `attrs` classes and `dataclasses`). It excels at recursively structuring and unstructuring data while supporting type hints and offering extensive customization via hooks. Releases are frequent, often including breaking changes across minor versions.

pip install cattrs
error ModuleNotFoundError: No module named 'typing_extensions'
cause `cattrs` sometimes conditionally imports `typing_extensions` for compatibility with older Python versions, but dependency resolution or specific `cattrs` versions can lead to this module being missing on systems where it's still needed.
fix
Install typing_extensions using pip: pip install typing_extensions.
error cattrs.errors.StructureHandlerNotFoundError: Cannot find a structuring handler for <class 'your.CustomClass'>
cause `cattrs` does not automatically know how to convert unstructured data (like a dictionary) into instances of arbitrary custom classes or complex types (e.g., `datetime` or a non-`attrs`/`dataclass` class) without a registered structuring hook.
fix
Register a custom structuring hook for the specific type with the cattrs converter. For example, for a datetime object: converter.register_structure_hook(datetime, lambda d, t: datetime.fromisoformat(d)).
error TypeError: Invalid first argument to `register()`. ForwardRef('MyClass') is not a class.
cause The `register_structure_hook` or `register_unstructure_hook` methods, which use `functools.singledispatch` internally, require an actual class type as their first argument, not a `ForwardRef` object.
fix
Ensure that all classes are fully defined before attempting to register hooks for them using ForwardRefs. If you must use ForwardRefs, resolve them to the actual class type using typing.get_type_hints or by passing the actual class directly after it's defined.
error ValueError: 'some_string' is not a valid MyEnum
cause When structuring data into an `Enum` type, the input value does not match any of the defined members (by value or name) of the target `Enum` class.
fix
Provide an input value that exactly matches one of the Enum members' values or names. If custom mapping or case-insensitivity is needed, register a custom structuring hook for the Enum type.
error cattrs.errors.ForbiddenExtraKeysError: While structuring <class 'your.MyClass'>, extra keys were found: {'unexpected_key'}
cause The `forbid_extra_keys=True` option is enabled on the `cattrs` converter or for the specific class being structured, and the input data contains keys that do not correspond to any attributes defined in the target class.
fix
Either remove the extra keys from your input data before structuring, or disable the forbid_extra_keys setting if these extra keys should be ignored. If the keys are legitimate, add them as attributes to your target class.
breaking As of v25.3.0, abstract sets (`collections.abc.Set`) are now structured into `frozenset` by default, instead of `set`. This might affect code expecting mutable sets.
fix If the previous behavior (structuring into `set`) is required, register a custom structure hook for `collections.abc.Set` on your converter instance (e.g., `converter.register_structure_hook(Set, lambda d, t: set(d))`).
breaking As of v25.2.0, sequences (`collections.abc.Sequence`) are now structured into `tuple` by default, instead of `list`. This change provides better immutability and consistency.
fix If the previous behavior (structuring into `list`) is required, register a custom structure hook for `collections.abc.Sequence` on your converter instance (e.g., `converter.register_structure_hook(Sequence, lambda d, t: list(d))`).
breaking As of v25.1.0, `StructureHandlerNotFoundError` is raised more eagerly (on hook creation rather than on first use). This helps surface missing hooks sooner.
fix Ensure all types intended for structuring have appropriate hooks registered *before* attempting to use the converter. Consult the `cattrs` migration guide for details on customizing `unstructure_hook_fallback_factory` if necessary.
breaking As of v24.1.0, unstructuring hooks for `typing.Any` now consistently use the runtime type of the value. Previously, this behavior was underspecified and inconsistent.
fix If your application relied on the previous inconsistent behavior for `typing.Any`, you may need to explicitly register custom `unstructure_hook`s for specific types or `typing.Any` to achieve the desired effect.
gotcha The top-level `cattrs.structure()` and `cattrs.unstructure()` functions operate on a global converter instance. Registering hooks or changing settings on this global converter can lead to unexpected side effects across different parts of an application or in library code.
fix For complex applications or when developing libraries, it is strongly recommended to create and manage your own `cattrs.Converter()` instance(s) and register hooks on those private instances to avoid polluting the global state.
python os / libc status wheel install import disk
3.10 alpine (musl) - - 0.12s 19.5M
3.10 slim (glibc) - - 0.08s 20M
3.11 alpine (musl) - - 0.17s 21.4M
3.11 slim (glibc) - - 0.18s 22M
3.12 alpine (musl) - - 0.14s 13.3M
3.12 slim (glibc) - - 0.14s 14M
3.13 alpine (musl) - - 0.13s 12.9M
3.13 slim (glibc) - - 0.13s 13M
3.9 alpine (musl) - - 0.11s 19.0M
3.9 slim (glibc) - - 0.10s 19M

Demonstrates basic structuring of a dictionary into an `attrs` class instance and unstructuring it back, using the global converter.

from attrs import define
from cattrs import structure, unstructure

@define
class User:
    id: int
    name: str
    email: str

# Unstructured data (e.g., from JSON)
unstructured_data = {"id": 1, "name": "Alice", "email": "alice@example.com"}

# Structure into a User instance
user_instance = structure(unstructured_data, User)
print(f"Structured: {user_instance}")

# Unstructure back to a dictionary
unstructured_output = unstructure(user_instance)
print(f"Unstructured: {unstructured_output}")