With cattrs version v24.1.2 the following test fails:
def test__cattrs_kw_only_propagation():
import dataclasses
import cattrs
@dataclasses.dataclass
class PartialKeywords:
a1: str = 'Default'
a2: str = dataclasses.field(kw_only=True)
converter = cattrs.Converter(detailed_validation=False)
assert converter.structure(
{
'a2': 'Value',
},
PartialKeywords,
) == PartialKeywords(
a1='Default',
a2='Value',
)
What is the test showing?
A dataclass class is defined, with the first attribute possibly positional, and the second attribute with kw_only=True. The first attribute has a default value, so it is optional.
When structuring the passed dict, cattrs is expected to honor the kw_only attribute setting. It does not.
Suggested root cause
The _compat module has functions to support dataclasses by converting some features to the corresponding attrs concepts. The function adapted_fields() does this for class attribute descriptors by creating cattrs Attribute instances from dataclass fields.
The Attribute creation lacks the kw_only argument and therefore uses the default kw_only=False.
"Unfortunately" the defect hits only in one scenario:
- When using
BaseConverter, this does not hit, as structure_attrs_fromdict always uses kw-based creation.
- When using
Converter with detailed validation enabled, the generated instantiation script is also always using kw-based creation.
- Only when using
Converter without detailed validation support, the generated instantion script depends on the kw_only setting of an attribute.
The latter distinction is part of Converter.make_dict_structure_n_from_attrs, where you find code like:
if a.kw_only:
invocation_line = f"{a.alias}={invocation_line}"
Options to fix
As only the non-detailed-validation case is currently respecting kw_only at all, this could could be removed to even in that case always use kw-based class creation in the instantiation script.
If that code should be kept, adapted_fields can possibly be fixed easily by propagating the kw_only setting:
Attribute(
attr.name,
...,
# FIX:
kw_only=attr.kw_only,
)
for attr in attrs
]
This patch fixes the test above.
With cattrs version v24.1.2 the following test fails:
What is the test showing?
A dataclass class is defined, with the first attribute possibly positional, and the second attribute with
kw_only=True. The first attribute has a default value, so it is optional.When structuring the passed dict, cattrs is expected to honor the
kw_onlyattribute setting. It does not.Suggested root cause
The
_compatmodule has functions to support dataclasses by converting some features to the corresponding attrs concepts. The functionadapted_fields()does this for class attribute descriptors by creating cattrsAttributeinstances from dataclass fields.The
Attributecreation lacks thekw_onlyargument and therefore uses the defaultkw_only=False."Unfortunately" the defect hits only in one scenario:
BaseConverter, this does not hit, asstructure_attrs_fromdictalways uses kw-based creation.Converterwith detailed validation enabled, the generated instantiation script is also always using kw-based creation.Converterwithout detailed validation support, the generated instantion script depends on thekw_onlysetting of an attribute.The latter distinction is part of
Converter.make_dict_structure_n_from_attrs, where you find code like:Options to fix
As only the non-detailed-validation case is currently respecting
kw_onlyat all, this could could be removed to even in that case always use kw-based class creation in the instantiation script.If that code should be kept,
adapted_fieldscan possibly be fixed easily by propagating thekw_onlysetting:This patch fixes the test above.