Closed petergaultney closed 1 year ago
So first of all, apologies for taking almost 5 years to respond. Must have slipped my radar somehow ;)
I don't think I'd like this in cattrs proper since it feels like a niche case. Just out of curiosity, I tried implementing this to see how difficult it'd actually be.
Here's my solution:
from typing import get_origin
from cattrs import Converter
c = Converter()
c.register_structure_hook_func(
lambda t: get_origin(t) is list, lambda v, t: c._structure_list(v, t) if v else []
)
print(c.structure(None, list[str]))
print(c.structure([1, 2], list[str]))
Maybe not as easy as I'd like, but also not super hard.
Description
Structuring a dictionary to a custom @attr.s type, with defaults provided for all optional arguments.
Cattrs fails to structure the object when there are non-null keys with null/None values where a List/Tuple/Set is expected, because None is not valid (it's not an
Optional[List[Foo]]
, it's just afoolist: List[Foo] = attr.Factory(list)
.Suggestion
This is clearly not a bug in cattrs. This is more of a feature request/possible PR: Would you be interested in supporting the use case where a Value of None for a non-Optional List/Tuple/Set in an
attrs
object would result in filling the attribute with its default value, instead of throwing an error?Basically, I would like for my
attrs
types to remain fairly pure - no 'Optional' typing annotation added all over the place - instead, I'd be able to rely on cattrs substituting the default empty list/set/tuple/whatever in the case where there was no underlying object to iterate over because it was null.Looking through the code, I think this could be supported only for
attrs
classes by checking instructure_attrs_fromdict
to see ifval is None
, and if so, checking ifa.default is not None
, and if so, using the default and/or the Factory to generate the 'expected' default.Again, I realize this is not a bug. Just wanted to raise the possibility. I could probably contribute a PR if it was likely to be accepted. There are already workarounds, like registering structure hooks for every List[Type] for which I want to enable this behavior, but it starts to get a little messy as more types get defined.