cattrs
Structuring and unstructuring library for attrs/dataclasses — converts between Python objects and primitive data (dicts, lists) for serialization. cattrs features: structure() for dict→object conversion, unstructure() for object→dict conversion, Converter for custom serialization rules, GenConverter with generics support, register_structure_hook/register_unstructure_hook for custom types, ClassValidationError with per-field errors, cattrs.preconf.json for JSON-ready converters, support for attrs, dataclasses, TypedDict, NamedTuple, and most typing constructs (Union, Optional, List, Dict), and exception groups for multiple field errors.
Score Breakdown
⚙ Agent Friendliness
🔒 Security
Pure serialization library. structure() deserializes untrusted data — validators in attrs classes run during structure(); ensure all attrs validators are properly defined. Unstructuring to dict does not sanitize data — validate before structuring from user input.
⚡ Reliability
Best When
attrs/dataclass serialization with custom types, complex nesting, and Union types — cattrs is the standard serialization companion for attrs classes.
Avoid When
API schema generation (use Pydantic), simple attrs serialization (use attrs.asdict()), or when Pydantic's integrated validation is needed.
Use Cases
- • Agent attrs to dict — import cattrs; import attrs; @attrs.define; class User: id: int; name: str; email: str; user = User(1, 'Alice', 'a@b.com'); d = cattrs.unstructure(user) — {'id': 1, 'name': 'Alice', 'email': 'a@b.com'}; user2 = cattrs.structure(d, User) — round-trip; agent converts attrs objects to dicts for JSON serialization
- • Agent nested structuring — @attrs.define; class Order: id: int; user: User; items: list[str]; data = {'id': 99, 'user': {'id': 1, 'name': 'Alice', 'email': 'a@b.com'}, 'items': ['book', 'pen']}; order = cattrs.structure(data, Order) — nested; agent structures nested dicts recursively; all nested attrs/dataclass fields are structured automatically
- • Agent custom converter — from cattrs import Converter; c = Converter(); c.register_unstructure_hook(datetime, lambda dt: dt.isoformat()); c.register_structure_hook(datetime, lambda v, _: datetime.fromisoformat(v)); d = c.unstructure(user_with_datetime) — custom types; agent registers converters for types cattrs doesn't handle by default (datetime, UUID, Decimal)
- • Agent JSON preset — from cattrs.preconf.json import make_json_converter; c = make_json_converter(); json_dict = c.unstructure(obj) — JSON-ready; agent uses pre-configured converter that handles datetime→ISO string, bytes→base64, UUID→string; unstructure produces JSON-serializable dict directly; then json.dumps(json_dict)
- • Agent validation errors — from cattrs import ClassValidationError; try: obj = cattrs.structure(data, MyClass) except ClassValidationError as e: for exc in e.exceptions: print(exc.exc_type, exc.path) — validation; agent catches structured exceptions with per-field error details; ClassValidationError has .exceptions list with path to each failed field
Not For
- • API serialization with schema generation — cattrs has no OpenAPI/JSON Schema output; for API use Pydantic which generates schemas
- • Validation with rich error messages — Pydantic v2 has better validation error messages for user-facing APIs
- • Simple dict serialization — for attrs without complex nesting, attrs.asdict() is sufficient; cattrs shines for complex types
Interface
Authentication
No auth — serialization library.
Pricing
cattrs is MIT licensed. Part of the attrs ecosystem. Free for all use.
Agent Metadata
Known Gotchas
- ⚠ Default converter is global and shared — cattrs.structure() uses a global default Converter; registering hooks on default converter affects all code using it; agent code: create a custom Converter() for app-specific hooks instead of modifying global; from cattrs import Converter; c = Converter() — isolated converter
- ⚠ Union types need disambiguation — cattrs.structure({'x': 1}, Union[ClassA, ClassB]) may fail without discriminator; cattrs tries each type in order; agent code with Union: add discriminator field or use register_structure_hook to implement custom Union logic; or use Literal-tagged unions with GenConverter
- ⚠ datetime not handled by default converter — cattrs.structure('2024-01-15T14:30:00', datetime) raises StructureError by default; agent code: use make_json_converter() from cattrs.preconf.json which handles datetime, UUID, bytes; or register_structure_hook(datetime, ...) on custom converter
- ⚠ unstructure returns plain dicts not JSON — cattrs.unstructure(obj) returns Python dict; may contain non-JSON-serializable types (datetime, UUID); json.dumps(cattrs.unstructure(obj)) may raise TypeError; agent code: use make_json_converter() which handles common non-JSON types; or register unstructure hooks for each special type
- ⚠ ClassValidationError requires explicit handling — cattrs.structure() with invalid data raises ClassValidationError (not ValueError/TypeError); agent code: catch ClassValidationError specifically to get field-level errors; generic except Exception misses the structured error info; import ClassValidationError from cattrs
- ⚠ Slots attrs classes need GenConverter — @attrs.define (slots=True by default) works with default Converter; but complex generic types (List[MyClass], Optional[MyClass]) work better with GenConverter: from cattrs import GenConverter; c = GenConverter() — generates optimized code for generic types; default Converter works for most cases
Alternatives
Full Evaluation Report
Detailed scoring breakdown, competitive positioning, security analysis, and improvement recommendations for cattrs.
Scores are editorial opinions as of 2026-03-06.