Maybe better overall than castfit.
📝 cattrs I: un/structuring speed (The Three Wands).
During the code review for castfit
, Gemini pointed me at attrs
and cattrs
. Many of the features of the attrs
became dataclasses
in the standard python library while the features of the cattrs
look very similar to my own castfit
library.
I started reading up on cattrs
and I came across the article above which explains the three phases of optimization that cattrs
went through. The first phase roughly corresponds to how castfit
does conversions today. It also highlights the same problems I anticipate if castfit
were to ever be used in a production environment.
The second (generated code) and third phases (optimized bytecode) reflect some very interesting optimizations. I'm glad that they kept the initial phase code around for situations where you don't have time up front (e.g., at CLI start up).
My simple test didn't work:
uv run --with cattrs python
>>> import cattrs
>>> class Cat:
... name: str
... age: int
...
>>> cattrs.structure({"name": "Garfield", "age": 45}, Cat)
Traceback (most recent call last):
File "<python-input-2>", line 1, in <module>
cattrs.structure({"name": "Garfield", "age": 45}, Cat)
~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File ".../lib/python3.13/site-packages/cattrs/converters.py", line 558, in structure
return self._structure_func.dispatch(cl)(obj, cl)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
File ".../lib/python3.13/site-packages/cattrs/fns.py", line 22, in raise_error
raise StructureHandlerNotFoundError(msg, type_=cl)
cattrs.errors.StructureHandlerNotFoundError: Unsupported type: <class '__main__.Cat'>. Register a structure hook for it.
It turns out you need to use attrs
or dataclasses
to make this into an object that can be converted into:
>>> from dataclasses import dataclass
>>> @dataclass
... class Cat:
... name: str
... age: int
...
>>> cattrs.structure({"name": "Garfield", "age": 45}, Cat)
Cat(name='Garfield', age=45)
castfit
can handle the dataclass approach too, but for the undecorated version it just tries constructing an empty object and setting a bunch of attributes on it. Not sure if this is a reasonable assumption (probably isn't), but it reduces the number of required imports.
Overall, though, I'm pretty impressed by cattrs
.