cattrs
cattrs copied to clipboard
Option to include `init=False` globally on a converter, not per class
- cattrs version: v23.2.3
- Python version: 3.11.6
- Operating System: Ubuntu
Description
On previous cattrs versions, I used to structure and unstructure a lot of attrs classes that had many init=False fields.
Now, though, with the new versions of cattrs (v23.2.0), this behavior changed, breaking my code.
I'd like to be able to enable the old behavior back globally on a converter, instead of having to enable it for every single class, as I have many classes all over the place that would benefit from re-enabling init=False fields in conversion.
import attrs
import cattrs
@attrs.define
class Foo:
bar: int
baz: str = attrs.field(init=False)
foo = Foo(bar=1)
foo.baz = "hello"
converter = cattrs.Converter()
print(converter.unstructure(foo))
# before: {'bar': 1, 'baz': 'hello'}
# after: {'bar': 1}
I thought about something like this:
import cattrs
from cattrs.strategies import include_init_false
converter = cattrs.Converter()
include_init_false(converter)
# OR
converter = cattrs.Converter(include_init_false=True)
I think I found a workaround on the docs, but it wasn't linked in the places I quoted:
- https://catt.rs/en/stable/unstructuring.html#unstructuring-hook-factories
I'll try it out and see if it solves all my problems - I think it likely will.
But anyway, I think it'd be nice to link this workaround to these places:
- https://catt.rs/en/stable/history.html#id4
- https://catt.rs/en/latest/customizing.html#include-init-false
(Also note that the first link includes a broken reference to the second one, which would also be nice to fix.)
Edit: Just noticed that the example is for the opposite case - to exclude init=False fields. But by changing it a tiny bit it does work to do what I want.
Edit: I simplified the code from the example, using newer features, and made it into a function:
import cattrs
from attrs import has
from cattrs.gen import make_dict_unstructure_fn
def include_init_false(c: cattrs.Converter) -> None:
c.register_unstructure_hook_factory(
has,
lambda cl: make_dict_unstructure_fn(
cl=cl,
converter=c,
_cattrs_include_init_false=True,
),
)
Howdy!
I think a converter parameter is almost certainly not the way to go, even though I've went that route before. Simply because the converter will end up a huge mess of different parameters.
Also the point of cattrs is to customize it with hooks and hook factories ;) I think your solution is pretty good.
Documentation is good though. I've been playing around with the idea of creating a migrations page in the docs. This would be a collection of recipes for dealing with breaking changes in cattrs, per version. And it would be in parallel with the changelog, just a different page. The idea being, if you're upgrading cattrs you can just look here for recipes on how to restore old behavior. I think this would be a good candidate for something like that.