-
-
Notifications
You must be signed in to change notification settings - Fork 118
Hookable attrs unstructuring #558
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
Comments
We don't need to rewrite the hook factory, we just need to wrap it ;) from attrs import has
from immutables import Map
from cattrs import Converter
from cattrs.gen import make_dict_unstructure_fn
c = Converter()
def my_attrs_hook_factory(cls):
base_hook = make_dict_unstructure_fn(cls, c)
def my_hook(instance):
return Map(base_hook(instance))
return my_hook
c.register_unstructure_hook_factory(has, my_attrs_hook_factory) Let me know if you have any more questions! |
Great, thank you!
I can actually use this in other scenarios, like unconditionally injecting type tags. |
Here is my type tagging factory example. Thankfully nothing fancy. import cattrs
import attrs
conv = cattrs.Converter()
def tag_attrs_hook_factory(cl):
base_hook = cattrs.gen.make_dict_unstructure_fn(cl, conv)
def hook(instance):
unstruct = base_hook(instance)
unstruct["_type"] = type(instance).__name__
return unstruct
return hook
conv.register_unstructure_hook_factory(attrs.has, tag_attrs_hook_factory)
@attrs.define
class Thing:
a: int
assert conv.unstructure(Thing(1)) == {
"_type" : "Thing",
"a" : 1,
} |
@Tinche Adding to the type tagging example above, custom metamethods. The problem is that the metamethods and tag factory function cannot mix. import cattrs
from cattrs.strategies import use_class_methods
import attrs
# Use a set of metamethods
@attrs.define
class Thing:
a: int
def _unstructure(self):
return {"a" : str(self.a)}
@classmethod
def _structure(cls, val):
return cls(a=int(val["a"]))
conv = cattrs.Converter()
use_class_methods(
conv,
"_structure",
"_unstructure",
)
assert conv.unstructure(Thing(1)) == {
"a" : "1",
}
def tag_attrs_hook_factory(cl):
base_hook = cattrs.gen.make_dict_unstructure_fn(cl, conv)
def hook(instance):
unstruct = base_hook(instance)
unstruct["_type"] = type(instance).__name__
return unstruct
return hook
tagging_conv_a = cattrs.Converter()
tagging_conv_a.register_unstructure_hook_factory(attrs.has, tag_attrs_hook_factory)
use_class_methods(
tagging_conv_a,
"_structure",
"_unstructure",
)
# THIS IS WRONG. Does not have the tag
assert tagging_conv_a.unstructure(Thing(1)) == {
"a" : "1",
}
tagging_conv_b = cattrs.Converter()
use_class_methods(
tagging_conv_b,
"_structure",
"_unstructure",
)
tagging_conv_b.register_unstructure_hook_factory(attrs.has, tag_attrs_hook_factory)
# THIS IS WRONG. Does not convert the sub value correctly via the metamethod
assert tagging_conv_b.unstructure(Thing(1)) == {
"_type" : "Thing",
"a" : 1,
} What I tried initially was to just inject the converter so it dispatches to the metamethods properly. But this causes infinite recursion, e.g.: def tag_attrs_hook_factory(cl):
converter = ... # via closure
def hook(instance, converter):
unstruct = converter.unstructure(instance)
unstruct["_type"] = type(instance).__name__
return unstruct
return hook Any ideas? I was thinking that the |
I am looking to unstructure attrs classes to
immutables.Map
instead of dicts.What would be the best way to do this?
That is other than rewriting the entire hook factories :)
The text was updated successfully, but these errors were encountered: