Skip to content

Ordering of registered hooks affecting outcome of serialization #630

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ngchihuan opened this issue Mar 4, 2025 · 2 comments · Fixed by #631
Closed

Ordering of registered hooks affecting outcome of serialization #630

ngchihuan opened this issue Mar 4, 2025 · 2 comments · Fixed by #631
Labels
documentation Documentation needed.
Milestone

Comments

@ngchihuan
Copy link

Hi cattrs team,
I encountered a seemingly quite surprising feature of cattrs, namely that the ordering of registered hooks could affect the serialization outcome of nested attrs or dataclasses.
See the two cases below.

I guess it would be not too surprising if one understands that make_dict_unstructure_fn relies on context information (what has been registered and what is in namespace, perhaps).

But I think this could be confusing behavior, and more information could be added to the documentation. Or perhaps, some different mechanism should be implemented to make the ordering does not matter.

cattrs version: 24.1.2
python version: 3.9.6

from __future__ import annotations
from cattrs.preconf.orjson import make_converter
import attrs
from cattrs.gen import make_dict_unstructure_fn


@attrs.define
class AModel:
    a: int


@attrs.define
class TopModel:
    top: AModel


converter = make_converter()

obj = TopModel(top=AModel(1))
converter.register_unstructure_hook(AModel, lambda obj: "A")
converter.register_unstructure_hook(
    TopModel, make_dict_unstructure_fn(TopModel, converter)
)
se = converter.unstructure(obj, TopModel) #output :{'top': 'A'}

If the order of hooks is reversed

converter.register_unstructure_hook(
    TopModel, make_dict_unstructure_fn(TopModel, converter)
)
converter.register_unstructure_hook(AModel, lambda obj: "A")
se = converter.unstructure(obj, TopModel) #output: {'top': {'a': 1}}
@Tinche
Copy link
Member

Tinche commented Mar 4, 2025

Whether the order of hook registration matters or not is technically dependent on which hooks you use, but the vast majority of default hooks uses early hook resolution, so in practice the order always matters.

I agree we should document it prominently. I'll add a section to the top of https://catt.rs/en/stable/customizing.html.

@Tinche Tinche added the documentation Documentation needed. label Mar 4, 2025
@Tinche Tinche added this to the 25.1 milestone Mar 4, 2025
@ngchihuan
Copy link
Author

@Tinche
Thanks a lot for the explanation, and I'm looking forward to the updated documentation.

@Tinche Tinche linked a pull request Mar 6, 2025 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Documentation needed.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants