File: test_unions.py

package info (click to toggle)
python-cattrs 25.3.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,812 kB
  • sloc: python: 12,236; makefile: 155
file content (109 lines) | stat: -rw-r--r-- 2,656 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
from typing import Union

import pytest
from attrs import define

from cattrs.converters import BaseConverter, Converter

from ._compat import is_py310_plus


@pytest.mark.parametrize("cls", (BaseConverter, Converter))
def test_custom_union_toplevel_roundtrip(cls: type[BaseConverter]):
    """
    Test custom code union handling.

    We override union unstructuring to add the class type, and union structuring
    to use the class type.
    """
    c = cls()

    @define
    class A:
        a: int

    @define
    class B:
        a: int

    c.register_unstructure_hook(
        Union[A, B], lambda o: {"_type": o.__class__.__name__, **c.unstructure(o)}
    )
    c.register_structure_hook(
        Union[A, B], lambda o, t: c.structure(o, A if o["_type"] == "A" else B)
    )

    inst = B(1)
    unstructured = c.unstructure(inst, unstructure_as=Union[A, B])
    assert unstructured["_type"] == "B"

    assert c.structure(unstructured, Union[A, B]) == inst


@pytest.mark.skipif(not is_py310_plus, reason="3.10 union syntax")
@pytest.mark.parametrize("cls", (BaseConverter, Converter))
def test_310_custom_union_toplevel_roundtrip(cls: type[BaseConverter]):
    """
    Test custom code union handling.

    We override union unstructuring to add the class type, and union structuring
    to use the class type.
    """
    c = cls()

    @define
    class A:
        a: int

    @define
    class B:
        a: int

    c.register_unstructure_hook(
        A | B, lambda o: {"_type": o.__class__.__name__, **c.unstructure(o)}
    )
    c.register_structure_hook(
        A | B, lambda o, t: c.structure(o, A if o["_type"] == "A" else B)
    )

    inst = B(1)
    unstructured = c.unstructure(inst, unstructure_as=A | B)
    assert unstructured["_type"] == "B"

    assert c.structure(unstructured, A | B) == inst


@pytest.mark.parametrize("cls", (BaseConverter, Converter))
def test_custom_union_clsfield_roundtrip(cls: type[BaseConverter]):
    """
    Test custom code union handling.

    We override union unstructuring to add the class type, and union structuring
    to use the class type.
    """
    c = cls()

    @define
    class A:
        a: int

    @define
    class B:
        a: int

    @define
    class C:
        f: Union[A, B]

    c.register_unstructure_hook(
        Union[A, B], lambda o: {"_type": o.__class__.__name__, **c.unstructure(o)}
    )
    c.register_structure_hook(
        Union[A, B], lambda o, t: c.structure(o, A if o["_type"] == "A" else B)
    )

    inst = C(A(1))
    unstructured = c.unstructure(inst)
    assert unstructured["f"]["_type"] == "A"

    assert c.structure(unstructured, C) == inst