From comments in #558. Custom metamethods and make_unstructure_dict_unstructure_fn cannot be used together:
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.:
I think that make_dict_unstructure_fn and friends should honor the metamethods for the passed in converter and break recursion. I'm not sure if this makes sense though... In any case this is a difficult case to handle generally.
From comments in #558. Custom metamethods and
make_unstructure_dict_unstructure_fn
cannot be used together:What I tried initially was to just inject the converter so it dispatches to the metamethods properly. But this causes infinite recursion, e.g.:
I think that
make_dict_unstructure_fn
and friends should honor the metamethods for the passed in converter and break recursion. I'm not sure if this makes sense though... In any case this is a difficult case to handle generally.