{"id":470,"library":"cattrs","title":"cattrs: Composable Attrs/Dataclass Structuring","description":"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.","status":"active","version":"26.1.0","language":"python","source_language":"en","source_url":"https://github.com/python-attrs/cattrs","tags":["data serialization","attrs","dataclasses","type hinting","validation"],"install":[{"cmd":"pip install cattrs","lang":"bash","label":"Install latest version"}],"dependencies":[],"imports":[{"symbol":"structure","correct":"from cattrs import structure"},{"symbol":"unstructure","correct":"from cattrs import unstructure"},{"symbol":"Converter","correct":"from cattrs import Converter"}],"quickstart":{"code":"from attrs import define\nfrom cattrs import structure, unstructure\n\n@define\nclass User:\n    id: int\n    name: str\n    email: str\n\n# Unstructured data (e.g., from JSON)\nunstructured_data = {\"id\": 1, \"name\": \"Alice\", \"email\": \"alice@example.com\"}\n\n# Structure into a User instance\nuser_instance = structure(unstructured_data, User)\nprint(f\"Structured: {user_instance}\")\n\n# Unstructure back to a dictionary\nunstructured_output = unstructure(user_instance)\nprint(f\"Unstructured: {unstructured_output}\")","lang":"python","description":"Demonstrates basic structuring of a dictionary into an `attrs` class instance and unstructuring it back, using the global converter."},"warnings":[{"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))`).","message":"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.","severity":"breaking","affected_versions":">=25.3.0"},{"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))`).","message":"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.","severity":"breaking","affected_versions":">=25.2.0"},{"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.","message":"As of v25.1.0, `StructureHandlerNotFoundError` is raised more eagerly (on hook creation rather than on first use). This helps surface missing hooks sooner.","severity":"breaking","affected_versions":">=25.1.0"},{"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.","message":"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.","severity":"breaking","affected_versions":">=24.1.0"},{"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.","message":"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.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-05-12T14:04:29.932Z","next_check":"2026-06-26T00:00:00.000Z","problems":[{"fix":"Install `typing_extensions` using pip: `pip install 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.","error":"ModuleNotFoundError: No module named 'typing_extensions'"},{"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))`.","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.","error":"cattrs.errors.StructureHandlerNotFoundError: Cannot find a structuring handler for <class 'your.CustomClass'>"},{"fix":"Ensure that all classes are fully defined before attempting to register hooks for them using `ForwardRef`s. If you must use `ForwardRef`s, resolve them to the actual class type using `typing.get_type_hints` or by passing the actual class directly after it's defined.","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.","error":"TypeError: Invalid first argument to `register()`. ForwardRef('MyClass') is not a 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.","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.","error":"ValueError: 'some_string' is not a valid MyEnum"},{"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.","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.","error":"cattrs.errors.ForbiddenExtraKeysError: While structuring <class 'your.MyClass'>, extra keys were found: {'unexpected_key'}"}],"ecosystem":"pypi","meta_description":null,"install_score":100,"install_tag":"verified","quickstart_score":80,"quickstart_tag":"verified","pypi_latest":null,"install_checks":{"last_tested":"2026-05-12","tag":"verified","tag_description":"installs cleanly on critical runtimes, fast import, recently tested","results":[{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.12,"mem_mb":4.2,"disk_size":"19.5M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.08,"mem_mb":4.2,"disk_size":"20M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.17,"mem_mb":4.2,"disk_size":"21.4M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.18,"mem_mb":4.2,"disk_size":"22M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.14,"mem_mb":4.1,"disk_size":"13.3M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.14,"mem_mb":4.1,"disk_size":"14M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.13,"mem_mb":3.9,"disk_size":"12.9M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.13,"mem_mb":3.7,"disk_size":"13M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.11,"mem_mb":4.2,"disk_size":"19.0M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.1,"mem_mb":4.2,"disk_size":"19M"}]},"quickstart_checks":{"last_tested":"2026-04-23","tag":"verified","tag_description":"quickstart runs on critical runtimes, recently tested","results":[{"runtime":"python:3.10-alpine","exit_code":0},{"runtime":"python:3.10-slim","exit_code":0},{"runtime":"python:3.11-alpine","exit_code":0},{"runtime":"python:3.11-slim","exit_code":0},{"runtime":"python:3.12-alpine","exit_code":0},{"runtime":"python:3.12-slim","exit_code":0},{"runtime":"python:3.13-alpine","exit_code":0},{"runtime":"python:3.13-slim","exit_code":0},{"runtime":"python:3.9-alpine","exit_code":0},{"runtime":"python:3.9-slim","exit_code":0}]}}