Getting the next structure hook
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 25/100
Research direction
The issue asks how to chain a pre-normalization step in front of structure hooks, and it mentions get_structure_hook, make_dict_structure_fn, register_structure_hook_factory and use_class_methods. Read how those hooks are resolved and why a generic like list[Foo] raises StructureHandlerNotFoundError, and check the linked attrs issue for the attrib.type string behaviour. Done would be a maintainer-agreed design for field converters running before structuring, with tests for the ensure_list case.
Written by the indexing model from the issue text.
Description
I am trying to wrap and combine attrs.Converter and the structure hook to have a pre-normalization step before the structure logic kicks in. There are 2 issues I hit
Pre-normalizing the class
Here the issue is that I cannot get the original (or next preferred) structure, roughly can simulate it with
use_class_methods(raw_converter, "from_raw", "to_raw")
@define
class Foo:
a: str
b: int = 1
@classmethod
def from_raw(cls, data, converter):
if isinstance(data, str):
data = {"a": data}
if not isinstance(data, dict):
raise TypeEroor("Unsupported")
original_structure = structure.get_structure_hook(cls)
return original_structure(data, cls)
The recursion here is rather obvious, but there is no way to get n layers further down in the preference to get the next structure hook. Even if it was to be hard-coded or only go one layer further would probably work for most cases.
Pre-normalizing the fields
This is most likely a PEBKAC, but I get StructureHandlerNotFoundError when trying to get the structure hook of a generic. Basically the end goal is to have
@define
class Foo:
a: str
def ensure_list(data):
if not isinstance(data, list):
data = [data]
return data
@define
class Bar:
foo: list[Foo] = field(converter=ensure_list)
@raw_converter.register_structure_hook_factory(has)
def structure_attrs_with_converter(cls, converter):
cls_structure = cattrs.gen.make_dict_structure_fn(
cls,
converter,
**{
attrib.name: override(struct_hook=attr_structure)
for attrib in attrs.fields(cls)
if (
attr_structure := structure_field_with_converter(
attrib=attrib,
converter=converter,
)
)
},
)
return cls_structure
def structure_field_with_converter(attrib, converter):
if not attrib.converter:
return None
original_structure = converter.get_structure_hook(attrib.type)
def structure(raw_value, _type):
value = attrib.converter(raw_value, None, attrib)
return original_structure(value, _type)
return structure
core of the issue here is that attrib.type given here is a str, not even a ForwardRef that I could later evaluate 🤔
Edit: Aha, it's an attrs bug https://github.com/python-attrs/attrs/issues/1596
Also I have tried to get attrs.Converter and cattrs to work nicely together, but so far this is the only way I found feasible. The issue is that the attrs.Converter must run before the main structure of the class, otherwise it will try to structure each element in the foo key before making sure it is a list in this example. If there are other ways to design this, that would be welcome, since this can get a bit messy.
This might be related to https://github.com/python-attrs/cattrs/issues/659?
- Dominant language
- Python
- Stars
- 1.1k
- Forks
- 171
- Avg merge
- 14h 46m
- Merged PRs (30d)
- 5
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing 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.
More from python-attrs/cattrs
-
Dataclass default_factory field treated as required in union disambiguationPossibly taken A pull request linked to this issue is open or already merged. Open
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
python-attrs/cattrs#796 ·
Maintainers usually reply within 1 day
-
decimal.Decimal not handled in preconf converters (json, pyyaml)Possibly taken @Binilkks claimed this 112 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
python-attrs/cattrs#761 · 2 comments ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 64/100
python-attrs/cattrs#513 · 4 comments ·
Maintainers usually reply within 1 day
-
Structure hook on a PEP 695 alias is ignored inside a passthrough unionPossibly taken A pull request linked to this issue is open or already merged. Open
Difficulty 3/5 1-2 days Newbie friendliness 62/100
python-attrs/cattrs#797 ·
Maintainers usually reply within 1 day
-
msgspec converter skips hooks and drops private fields in nested recursionPossibly taken A pull request linked to this issue is open or already merged. Open
Difficulty 3/5 Half a day Newbie friendliness 64/100
python-attrs/cattrs#795 ·
Maintainers usually reply within 1 day
All issues in python-attrs/cattrs
Similar issues
-
docs: the dev-setup table and AGENTS.md name the reviewed_by guard that CI's Changelog job runsOpenarea: tools documentation good first issue ready for a contributor
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
eyalgolan/statecraft-israel#873 · 1 comment ·
Maintainers usually reply within 1 day
-
enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 5 days
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day
-
bug good first issue
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day