Skip to content

Using several factories simultaneously #659

@AdrianSosic

Description

@AdrianSosic

Hi @Tinche, my previous issue was basically pointless but I'm now getting closer to the heart of my problem. While I think I understand how regular hooks can be composed, it's not quite clear to me how somethign similar can be achieved using predicates/factories.

In essence, it would help me to better understand how hooks/predicates/factories interplay and how it is possible to "activate" certain customizations at once. The problem that I get with factories is that only ever one of them is active. Here a little example what I mean:

import cattrs
from attrs import define

converter = cattrs.Converter()


@define
class Base:
    x: int = 0


@define
class Sub(Base):
    y: int = 1


@define
class SubSub(Sub):
    z: int = 1


@define
class Container:
    contains: Base


@converter.register_unstructure_hook
def some_special_configuration(obj: SubSub) -> dict:
    fn = cattrs.gen.make_dict_unstructure_fn(
        SubSub, converter, x=cattrs.override(rename="renamed")
    )
    return fn(obj)


@converter.register_unstructure_hook_factory(lambda c: c is Base)
def add_type(_):
    def hook(obj):
        hook = cattrs.gen.make_dict_unstructure_fn(type(obj), converter)
        return {"type": obj.__class__.__name__, **hook(obj)}

    return hook


@converter.register_unstructure_hook_factory(lambda c: issubclass(c, Sub))
def indicate_if_sub(_):
    def hook(obj):
        hook = cattrs.gen.make_dict_unstructure_fn(type(obj), converter)
        return {"is_sub": obj.__class__.__name__, **hook(obj)}

    return hook


contains_base = Container(Base())
contains_sub = Container(Sub())
contains_subsub = Container(SubSub())

print(converter.unstructure(contains_base))
print(converter.unstructure(contains_sub))
print(converter.unstructure(contains_subsub))

Gives:

{'contains': {'type': 'Base', 'x': 0}}
{'contains': {'type': 'Sub', 'x': 0, 'y': 1}}
{'contains': {'type': 'SubSub', 'x': 0, 'y': 1, 'z': 1}}

How can I change the logic such that all rules are "active" at the same time, that is:

  • Because everything is unstructured as Base, the type field should be always present.
  • Both sub and subsub are of type Sub, meaning I want to get their additional is_sub field.
  • Finally, subsub has a special renaming that should be considered.

So what I want is rather:

{'contains': {'type': 'Base', 'x': 0}}
{'contains': {'type': 'Sub', 'is_sub': 'Sub', 'x': 0, 'y': 1}}
{'contains': {'type': 'SubSub', 'is_sub': 'SubSub', 'renamed': 0, 'y': 1, 'z': 1}}

I'd really appreciate if you could point me to the right track 🙃

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