Closed kkg-else42 closed 5 months ago
Sure. Which version of Python are you on?
It's 3.11
Ok, so this is the starting situation:
from attrs import define
from cattrs import Converter
from cattrs.strategies import configure_tagged_union
@define
class A:
a: int
@define
class B:
b: str
c = Converter()
configure_tagged_union(A | B, c)
but now you need to override the hook for B
to be something else:
def structure_b(val, type):
return B(val["c"]) # Arbitrary, could be anything
The simplest solution is to override the hook for B
before you configure the tagged union:
c = Converter()
c.register_structure_hook(B, structure_b)
configure_tagged_union(A | B, c)
print(c.structure({"_type": "B", "c": "a_string"}, A | B))
This will also change how B
is structured in all other contexts. If that's a problem you can have a separate converter just for this structuring; converters are cheap.
Another solution would be to use a NewType. In this case, you hook your custom structure function to the NewType instead of the original class, and your union contains the NewType. At runtime, it'll be an instance of B
. You also need to override the tag_generator
.
from typing import NewType
NewTypeB = NewType("NewTypeB", B)
c = Converter()
c.register_structure_hook(NewTypeB, structure_b)
configure_tagged_union(A | NewTypeB, c, tag_generator={A: "A", NewTypeB: "B"}.get)
print(c.structure({"_type": "B", "c": "a_string"}, A | NewTypeB))
A third solution is you apply a little pre-processing to the tagged union hook. Getting the hook involves accessing a private member (Converter._union_struct_registry
) but not to worry - this is going to be a public API in the next release (called get_structure_hook
).
c = Converter()
configure_tagged_union(A | B, c)
existing_hook = c._union_struct_registry[A | B]
def preprocess(value, type):
if value["_type"] == "B":
return structure_b(value, type)
return existing_hook(value, type)
c.register_structure_hook(A | B, preprocess) # Overwrite with our own
print(c.structure({"_type": "B", "c": "a_string"}, A | B))
All of these solve the issue, I think.
The simplest solution is to override the hook for
B
before you configure the tagged union:
Wow, just consider the order. It can be so easy if you know what you're doing. What a great tool! (I wish I had asked a few days earlier.) It might be good to include this information in the docs. ;-)
A third solution is you apply a little pre-processing to the tagged union hook. Getting the hook involves accessing a private member (
Converter._union_struct_registry
) but not to worry - this is going to be a public API in the next release (calledget_structure_hook
).
In my own limited solution attempts, I had exactly this feeling: Somehow I have to get into this (without really knowing how). Good to know that there will soon be an official way to do this.
All of these solve the issue, I think.
Yes and yes and yes. Thank you very much for your help and work! Greatly appreciated!
Hey there,
I've been using tagged_union strategy for a while now. Always with the defaults -- nothing special so far.
But now I have the need for a customized structuring/destructuring of one particular member of the union. All other members should continue to be processed with defaults.
It would be great if you could jumpstart my thoughts with a little example how this could be achieved.