Skip to content

Union with subclasses #583

Open
Open
@csqzhang

Description

@csqzhang
  • cattrs version: 24.1.2
  • Python version: 3.11
  • Operating System: Linux/MacOSX

Description

I am not sure how to handle union with subclass.

What I Did

import attr
import cattr
from cattrs.strategies import configure_tagged_union, include_subclasses


@attr.s(frozen=True, auto_attribs=True)
class A:
    pass


@attr.s(frozen=True, auto_attribs=True)
class B:
    pass


@attr.s(frozen=True, auto_attribs=True)
class B2(B):
    b: int



@attr.s(frozen=True, auto_attribs=True)
class C:
    x: A | B


CONVERTER = cattr.Converter()
configure_tagged_union(A | B, CONVERTER)
union_strategy = partial(configure_tagged_union, tag_name="type_name")
include_subclasses(B, CONVERTER, union_strategy=union_strategy)

if __name__ == "__main__":
    instance = C(x=B2(b=1))
    r = CONVERTER.unstructure(instance, unstructure_as=C)
    print(r)
    print(CONVERTER.structure(r, C))

Getting error as

Traceback (most recent call last):
  File "..../IPython/core/interactiveshell.py", line 3577, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-1-8e14e71c7dee>", line 1, in <module>
    CONVERTER.unstructure(instance, unstructure_as=C)
  File ".../python3.11/site-packages/cattrs/converters.py", line 300, in unstructure
    return self._unstructure_func.dispatch(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<cattrs generated unstructure __main__.C>", line 3, in unstructure_C
    'x': __c_unstr_x(instance.x),
          ^^^^^^^^^^^^^^^^^^^^^^^
  File ".../python3.11/site-packages/cattrs/strategies/_unions.py", line 82, in unstructure_tagged_union
    res = _exact_cl_unstruct_hooks[val.__class__](val)
          ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^
KeyError: <class '__main__.B2'>
^CTraceback (most recent call last):
  File ".../example.py", line 99, in <module>
    r = CONVERTER.unstruct

My understanding is that configure_tagged_union does not register all the hooks for subclasses. I wonder what is the right/best way to workaround it?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions