python-attrs / python-attrs/cattrs
Using several factories simultaneously
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 1.1k
- Forks
- 159
- Avg merge
- 12h 21m
- Merged PRs (30d)
- 6
Description
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, thetypefield should be always present. - Both
subandsubsubare of typeSub, meaning I want to get their additionalis_subfield. - Finally,
subsubhas 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 🙃
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the example and tracing register_unstructure_hook_factory, unstructure, and make_dict_unstructure_fn to understand how predicates and factories are selected. Determine whether the requested composition is supported by the existing API or needs a design decision. Done means Base, Sub, and SubSub produce the three expected dictionaries with type, is_sub, and renamed fields applied together.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100