File: test_optionals.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 (75 lines) | stat: -rw-r--r-- 2,018 bytes parent folder | download | duplicates (2)
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
from typing import Any, NewType, Optional

import pytest
from attrs import define

from cattrs import BaseConverter, Converter

from ._compat import is_py310_plus


def test_newtype_optionals(genconverter):
    """Newtype optionals should work."""
    Foo = NewType("Foo", str)

    genconverter.register_unstructure_hook(Foo, lambda v: v.replace("foo", "bar"))

    @define
    class ModelWithFoo:
        total_foo: Foo
        maybe_foo: Optional[Foo]

    assert genconverter.unstructure(ModelWithFoo(Foo("foo"), Foo("is it a foo?"))) == {
        "total_foo": "bar",
        "maybe_foo": "is it a bar?",
    }


@pytest.mark.skipif(not is_py310_plus, reason="3.10+ union syntax")
def test_newtype_modern_optionals(genconverter):
    """Newtype optionals should work."""
    Foo = NewType("Foo", str)

    genconverter.register_unstructure_hook(Foo, lambda v: v.replace("foo", "bar"))

    @define
    class ModelWithFoo:
        total_foo: Foo
        maybe_foo: Foo | None

    assert genconverter.unstructure(ModelWithFoo(Foo("foo"), Foo("is it a foo?"))) == {
        "total_foo": "bar",
        "maybe_foo": "is it a bar?",
    }


def test_optional_any(converter: Converter):
    """Unstructuring Any|None is equivalent to unstructuring as v.__class__."""

    @define
    class A:
        pass

    assert converter.unstructure(A(), Optional[Any]) == {}


def test_override_optional(converter: BaseConverter):
    """Optionals can be overridden using singledispatch."""

    @converter.register_structure_hook
    def _(val, _) -> Optional[int]:
        if val in ("", None):
            return None
        return int(val)

    assert converter.structure("", Optional[int]) is None
    assert converter.structure("1", Optional[int]) == 1

    @converter.register_unstructure_hook
    def _(val: Optional[int]) -> Any:
        if val in (None, 0):
            return None
        return val

    assert converter.unstructure(0, Optional[int]) is None
    assert converter.unstructure(5, Optional[int]) == 5