Skip to content

forbid_extra_keys doesn't work with override(rename=...) #190

@anthrotype

Description

@anthrotype
  • cattrs version: 1.9.0
  • Python version: 3.10.0
  • Operating System: macOS 11.6 (x86_64)

Description

I would like to have cattrs strip the leading underscore in attribute names, both when unstructuring and when structuring.
I registered two hook factories that pass override(rename=a.name[1:] if a.name[0] == "_"). However when I also set forbid_extra_keys=True, I get an error saying "Extra fields in constructor for ...", for all the underscore-prefixed attributes.

Basically the forbid_extra_keys feature is ignoring the rename overrides, and the two features don't work together.

What I Did

from attr import define, has, fields
from cattr.gen import make_dict_unstructure_fn, make_dict_structure_fn, override
from cattr.converters import GenConverter


def strip_underscore_unstructure(cls):
    return make_dict_unstructure_fn(
        cls,
        converter,
        **{a.name: override(rename=a.name[1:]) for a in fields(cls) if a.name[0] == "_"}
    )


def strip_underscore_structure(cls):
    return make_dict_structure_fn(
        cls,
        converter,
        _cattrs_forbid_extra_keys=True,
        **{a.name: override(rename=a.name[1:]) for a in fields(cls) if a.name[0] == "_"}
    )


converter = GenConverter(forbid_extra_keys=True)
converter.register_structure_hook_factory(has, strip_underscore_structure)
converter.register_unstructure_hook_factory(has, strip_underscore_unstructure)


@define
class A:
    b: int
    _c: str

    @property
    def c(self):
        return self._c


a = A(1, "abc")


print(converter.unstructure(a))  # {'b': 1, 'c': 'abc'}


# Traceback (most recent call last):
#   File "/Users/clupo/Github/ufoLib2/test_cattrs.py", line 43, in <module>
#     converter.structure(converter.unstructure(a), A)
#   File "/Users/clupo/Github/cattrs/src/cattr/converters.py", line 300, in structure
#     return self._structure_func.dispatch(cl)(obj, cl)
#   File "<cattrs generated structure __main__.A>", line 8, in structure_A
# Exception: Extra fields in constructor for A: c
converter.structure(converter.unstructure(a), A)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions