def test_union_with_union_in_nested_types(): @deserialize @serialize @dataclass class A: v: Union[UUID, List[Union[UUID, int]]] a_uuid = A([UUID("00611ee9-7ca3-41d3-9607-ea7268e264ea")]) assert to_dict(a_uuid, reuse_instances=False) == { "v": ["00611ee9-7ca3-41d3-9607-ea7268e264ea"] } assert a_uuid == from_dict(A, to_dict(a_uuid, reuse_instances=False), reuse_instances=False) assert a_uuid == from_dict(A, to_dict(a_uuid, reuse_instances=True), reuse_instances=True) a_int = A([1]) assert to_dict(a_int) == {"v": [1]} assert a_int == from_dict(A, to_dict(a_int, reuse_instances=False), reuse_instances=False) assert a_int == from_dict(A, to_dict(a_int, reuse_instances=True), reuse_instances=True)
def test_dataclass_inheritance(): @deserialize @serialize @dataclass class Base: i: int s: str @deserialize @serialize @dataclass class DerivedA(Base): j: int @deserialize @serialize @dataclass class DerivedB(Base): k: float # each class should have own scope # ensure the generated code of DerivedB does not overwrite the earlier generated code from DerivedA assert getattr(Base, serde.core.SERDE_SCOPE) is not getattr(DerivedA, serde.core.SERDE_SCOPE) assert getattr(DerivedA, serde.core.SERDE_SCOPE) is not getattr(DerivedB, serde.core.SERDE_SCOPE) base = Base(i=0, s="foo") assert base == from_dict(Base, to_dict(base)) a = DerivedA(i=0, s="foo", j=42) assert a == from_dict(DerivedA, to_dict(a)) b = DerivedB(i=0, s="foo", k=42.0) assert b == from_dict(DerivedB, to_dict(b))
def test_skip_if_default(se, de): @serde.serde class Foo: a: str = serde.field(default='foo', skip_if_default=True) f = Foo() assert f == de(Foo, se(f)) assert serde.to_dict(Foo()) == {} assert serde.to_dict(Foo('bar')) == {'a': 'bar'} assert serde.from_dict(Foo, {}) == Foo() assert serde.from_dict(Foo, {'a': 'bar'}) == Foo('bar')
def test_serde_with_lazy_type_annotations(): a = A(1, Status.ERR, ["foo"]) a_dict = {"a": 1, "b": "err", "c": ["foo"]} assert a == from_dict(A, a_dict) assert a_dict == to_dict(a) b = B(a, ("foo", a), Status.OK) b_dict = {"a": a_dict, "b": ("foo", a_dict), "c": "ok"} assert b == from_dict(B, b_dict) assert b_dict == to_dict(b)
def test_union_in_other_type(): @serde class A: v: Dict[str, Union[UUID, int]] a_uuid = A({"key": UUID("00611ee9-7ca3-41d3-9607-ea7268e264ea")}) assert a_uuid == from_dict(A, to_dict(a_uuid, reuse_instances=False), reuse_instances=False) assert a_uuid == from_dict(A, to_dict(a_uuid, reuse_instances=True), reuse_instances=True) a_int = A({"key": 1}) assert a_int == from_dict(A, to_dict(a_int, reuse_instances=False), reuse_instances=False) assert a_int == from_dict(A, to_dict(a_int, reuse_instances=True), reuse_instances=True)
def test_optional_union_with_complex_types(): @serde class A: v: Optional[Union[int, IPv4Address, UUID]] a = A(123) assert a == from_dict(A, to_dict(a, reuse_instances=False), reuse_instances=False) assert a == from_dict(A, to_dict(a, reuse_instances=True), reuse_instances=True) a_none = A(None) assert a_none == from_dict(A, to_dict(a_none, reuse_instances=False), reuse_instances=False) assert a_none == from_dict(A, to_dict(a_none, reuse_instances=True), reuse_instances=True)
def test_default(se, de): p = PriDefault() assert p == de(PriDefault, se(p)) p = PriDefault() assert p == from_dict(PriDefault, {}) assert p == from_dict(PriDefault, {'i': 10}) assert p == from_dict(PriDefault, {'i': 10, 's': 'foo'}) assert p == from_dict(PriDefault, {'i': 10, 's': 'foo', 'f': 100.0}) assert p == from_dict(PriDefault, {'i': 10, 's': 'foo', 'f': 100.0, 'b': True}) assert 10 == dataclasses.fields(PriDefault)[0].default assert 'foo' == dataclasses.fields(PriDefault)[1].default assert 100.0 == dataclasses.fields(PriDefault)[2].default assert True is dataclasses.fields(PriDefault)[3].default
def test_exception_on_not_supported_types(): class UnsupportedClass: def __init__(self): pass @serde.serde class Foo: b: UnsupportedClass with pytest.raises(serde.SerdeError) as se_ex: serde.to_dict(Foo(UnsupportedClass())) assert str(se_ex.value).startswith("Unsupported type: UnsupportedClass") with pytest.raises(serde.SerdeError) as de_ex: serde.from_dict(Foo, {"b": UnsupportedClass()}) assert str(de_ex.value).startswith("Unsupported type: UnsupportedClass")
def test_union_rename_all(): @serde(rename_all='pascalcase') class Foo: bar_baz: Union[int, str] assert to_dict(Foo(10)) == {'BarBaz': 10} assert from_dict(Foo, {'BarBaz': 'foo'}) == Foo('foo')
def test_union_with_complex_types_and_reuse_instances(): @serde(reuse_instances_default=True) class A: v: Union[int, IPv4Address, UUID] a_int = A(1) a_int_roundtrip = from_dict(A, to_dict(a_int)) assert a_int == a_int_roundtrip assert a_int.v is a_int_roundtrip.v a_ip = A(IPv4Address("127.0.0.1")) a_ip_roundtrip = from_dict(A, to_dict(a_ip)) assert a_ip == a_ip_roundtrip assert a_ip.v is a_ip_roundtrip.v a_uid = A(UUID("a317958e-4cbb-4213-9f23-eaff1563c472")) a_uid_roundtrip = from_dict(A, to_dict(a_uid)) assert a_uid == a_uid_roundtrip assert a_uid.v is a_uid_roundtrip.v
def test_union_with_union_in_nested_tuple(): @serde class A: v: Union[bool, Tuple[Union[str, int]]] a_bool = A(False) a_bool_dict = {"v": False} assert to_dict(a_bool) == a_bool_dict assert from_dict(A, a_bool_dict) == a_bool a_str = A(("a",)) a_str_dict = {"v": ("a",)} assert to_dict(a_str) == a_str_dict assert from_dict(A, a_str_dict) == a_str a_int = A((1,)) a_int_dict = {"v": (1,)} assert to_dict(a_int) == a_int_dict assert from_dict(A, a_int_dict) == a_int
def test_dataclass_default_factory(se, de): @serde.serde class Foo: foo: str items: Dict[str, int] = serde.field(default_factory=dict) f = Foo('bar') assert f == de(Foo, se(f)) assert {'foo': 'bar', 'items': {}} == serde.to_dict(f) assert f == serde.from_dict(Foo, {'foo': 'bar'})
def test_from_dict(): p = Pri(10, 'foo', 100.0, True) d = {'i': 10, 's': 'foo', 'f': 100.0, 'b': True} assert d == asdict(p) assert p == from_dict(Pri, d) p = {'p': Pri(10, 'foo', 100.0, True)} d = {'p': {'i': 10, 's': 'foo', 'f': 100.0, 'b': True}} assert d == asdict(p) assert p == from_dict(Dict[str, Pri], d) p = [Pri(10, 'foo', 100.0, True)] d = ({'i': 10, 's': 'foo', 'f': 100.0, 'b': True}, ) assert d == asdict(p) assert p == from_dict(List[Pri], d) p = (Pri(10, 'foo', 100.0, True), ) d = ({'i': 10, 's': 'foo', 'f': 100.0, 'b': True}, ) assert d == asdict(p) assert p == from_dict(Tuple[Pri], d)
def test_make_serialize_deserialize(se): fields = [('i', int, dataclasses.field())] Foo = make_serde('Foo', se, fields) f = Foo(10) assert serde.to_dict(f) == {'i': 10} assert str(f) == 'Foo(i=10)' assert serde.from_dict(Foo, {'i': 10}) == f # Test class attribute fields = [('int_field', int, dataclasses.field())] Foo = make_serde('Foo', se, fields, rename_all='pascalcase') f = Foo(10) assert serde.to_dict(f) == {'IntField': 10} # Test field attribute fields = [('i', int, dataclasses.field(metadata={'serde_skip': True})), ('j', float, dataclasses.field())] Foo = make_serde('Foo', se, fields) f = Foo(10, 100.0) assert serde.to_dict(f) == {'j': 100.0} # Test class/field attributes at the same time fields = [ ('int_field', int, dataclasses.field(metadata={'serde_rename': 'renamed_field'})), ('float_field', float, dataclasses.field()), ] Foo = make_serde('Foo', se, fields, rename_all='pascalcase') f = Foo(10, 100.0) assert serde.to_dict(f) == {'renamed_field': 10, 'FloatField': 100.0} # Nested fields = [('v', int, dataclasses.field())] Bar = make_serde('Bar', se, fields) fields = [('bar', Bar, dataclasses.field())] Foo = make_serde('Foo', se, fields) f = Foo(Bar(10)) assert serde.to_dict(f) == {'bar': {'v': 10}} assert serde.from_dict(Foo, {'bar': {'v': 10}}) == f
def test_union_with_complex_types_in_containers(): @deserialize @serialize @dataclass class A: v: Union[List[IPv4Address], List[UUID]] a_ips = A([IPv4Address("127.0.0.1"), IPv4Address("10.0.0.1")]) assert a_ips == from_dict(A, to_dict(a_ips, reuse_instances=False), reuse_instances=False) assert a_ips == from_dict(A, to_dict(a_ips, reuse_instances=True), reuse_instances=True) a_uids = A([ UUID("9c244009-c60d-452b-a378-b8afdc0c2d90"), UUID("5831dc09-20fe-4433-b476-5866b7143364") ]) assert a_uids == from_dict(A, to_dict(a_uids, reuse_instances=False), reuse_instances=False) assert a_uids == from_dict(A, to_dict(a_uids, reuse_instances=True), reuse_instances=True) a_empty = A([]) assert a_empty == from_dict(A, to_dict(a_empty, reuse_instances=False), reuse_instances=False) assert a_empty == from_dict(A, to_dict(a_empty, reuse_instances=True), reuse_instances=True)
def test_union_in_union(): @deserialize @serialize @dataclass class A: v: Union[UUID, Union[int, str]] a_uuid = A(UUID("00611ee9-7ca3-41d3-9607-ea7268e264ea")) assert a_uuid == from_dict(A, to_dict(a_uuid, reuse_instances=False), reuse_instances=False) assert a_uuid == from_dict(A, to_dict(a_uuid, reuse_instances=True), reuse_instances=True) a_int = A(1) assert a_int == from_dict(A, to_dict(a_int, reuse_instances=False), reuse_instances=False) assert a_int == from_dict(A, to_dict(a_int, reuse_instances=True), reuse_instances=True) a_str = A("hello") assert a_str == from_dict(A, to_dict(a_str, reuse_instances=False), reuse_instances=False) assert a_str == from_dict(A, to_dict(a_str, reuse_instances=True), reuse_instances=True)
def test_union_with_list_of_other_class(): @serde class A: a: int @serde class B: b: Union[List[A], str] b = B([A(1)]) b_dict = {"b": [{"a": 1}]} assert to_dict(b) == b_dict assert from_dict(B, b_dict) == b
def test_exception_on_not_supported_types(): class UnsupportedClass: def __init__(self): pass @deserialize @serialize @dataclass class Foo: b: UnsupportedClass with pytest.raises(SerdeError) as se_ex: to_dict(Foo(UnsupportedClass())) assert str(se_ex.value).startswith( "Unsupported type: <class \'tests.test_basics.test_exception_on_not_supported_types.<locals>.UnsupportedClass\'>" ) with pytest.raises(SerdeError) as de_ex: from_dict(Foo, {"b": UnsupportedClass()}) assert str(de_ex.value).startswith( "Unsupported type: <class \'tests.test_basics.test_exception_on_not_supported_types.<locals>.UnsupportedClass\'>" )
def test_dataclass_default_factory(se, de): @deserialize @serialize @dataclass class Foo: foo: str items: Dict[str, int] = field(default_factory=dict) f = Foo('bar') assert f == de(Foo, se(f)) assert {'foo': 'bar', 'items': {}} == to_dict(f) assert f == from_dict(Foo, {'foo': 'bar'})
def test_union_with_complex_types(): @serde class A: v: Union[int, IPv4Address, UUID] a_int = A(1) a_int_json = '{"v": 1}' assert to_json(a_int) == a_int_json assert from_json(A, a_int_json) == a_int assert a_int == from_dict(A, to_dict(a_int)) a_ip = A(IPv4Address("127.0.0.1")) a_ip_json = '{"v": "127.0.0.1"}' assert to_json(a_ip) == a_ip_json assert from_json(A, a_ip_json) == a_ip assert a_ip == from_dict(A, to_dict(a_ip)) a_uid = A(UUID("a317958e-4cbb-4213-9f23-eaff1563c472")) a_uid_json = '{"v": "a317958e-4cbb-4213-9f23-eaff1563c472"}' assert to_json(a_uid) == a_uid_json assert from_json(A, a_uid_json) == a_uid assert a_uid == from_dict(A, to_dict(a_uid))
def get(model: Type[T], afqn: str) -> Optional[T]: """Create a Python object of type Type[T] deserializing configuration file of the class which fully qualified name is afqn. The path of the configuration file is compute by the config_file_name function :param model: Type use a model to create a Python object deserializing a configuration file :type model: Type[T] :param afqn: fully qualified name of the class to configure :type afqn: str :return: an instance of Type[T] created deserializing the configuration file :rtype: T """ name = config_file_name(afqn) if not exists(name): return None with open(name) as c: return from_dict(model, TomlDeserializer.deserialize(c.read()))
def test_default(se, de): from serde import from_dict, from_tuple from .data import OptDefault, PriDefault p = PriDefault() assert p == de(PriDefault, se(p)) p = PriDefault() assert p == from_dict(PriDefault, {}) assert p == from_dict(PriDefault, {'i': 10}) assert p == from_dict(PriDefault, {'i': 10, 's': 'foo'}) assert p == from_dict(PriDefault, {'i': 10, 's': 'foo', 'f': 100.0}) assert p == from_dict(PriDefault, { 'i': 10, 's': 'foo', 'f': 100.0, 'b': True }) assert p == from_tuple(PriDefault, (10, 'foo', 100.0, True)) o = OptDefault() assert o == de(OptDefault, se(o)) o = OptDefault() assert o == from_dict(OptDefault, {}) assert o == from_dict(OptDefault, {"n": None}) assert o == from_dict(OptDefault, {"n": None, "i": 10}) assert o == from_tuple(OptDefault, (None, 10)) o = OptDefault(n=None, i=None) assert o == from_dict(OptDefault, {"n": None, "i": None}) assert o == from_tuple(OptDefault, (None, None)) assert 10 == dataclasses.fields(PriDefault)[0].default assert 'foo' == dataclasses.fields(PriDefault)[1].default assert 100.0 == dataclasses.fields(PriDefault)[2].default assert True is dataclasses.fields(PriDefault)[3].default
def test_union_exception_if_nothing_matches(): @deserialize @serialize @dataclass class A: v: Union[IPv4Address, UUID] with pytest.raises(SerdeError) as ex1: from_dict(A, {"v": "not-ip-or-uuid"}) assert str(ex1.value) == ( "Can not deserialize 'not-ip-or-uuid' of type str into Union[IPv4Address, UUID].\n" "Reasons:\n" " Failed to deserialize into IPv4Address: Expected 4 octets in 'not-ip-or-uuid'\n" " Failed to deserialize into UUID: badly formed hexadecimal UUID string" ) with pytest.raises(SerdeError) as ex2: from_dict(A, {"v": "not-ip-or-uuid"}, reuse_instances=True) assert str(ex2.value) == ( "Can not deserialize 'not-ip-or-uuid' of type str into Union[IPv4Address, UUID].\n" "Reasons:\n" " Failed to deserialize into IPv4Address: Expected 4 octets in 'not-ip-or-uuid'\n" " Failed to deserialize into UUID: badly formed hexadecimal UUID string" ) with pytest.raises(SerdeError) as ex3: from_dict(A, {"v": None}) # omit reason because it is not the same for all python versions & operating systems assert str(ex3.value).startswith( "Can not deserialize None of type NoneType into Union[IPv4Address, UUID]." ) with pytest.raises(SerdeError) as ex4: to_dict(A("not-ip-or-uuid")) assert str( ex4.value ) == "Can not serialize 'not-ip-or-uuid' of type str for Union[IPv4Address, UUID]" with pytest.raises(SerdeError) as ex5: to_dict(A("not-ip-or-uuid"), reuse_instances=True) assert str( ex5.value ) == "Can not serialize 'not-ip-or-uuid' of type str for Union[IPv4Address, UUID]" with pytest.raises(SerdeError) as ex6: to_dict(A(None), reuse_instances=True) assert str( ex6.value ) == "Can not serialize None of type NoneType for Union[IPv4Address, UUID]"
def test_string_forward_reference_works(): h = ForwardReferenceFoo(bar=ForwardReferenceBar(i=10)) h_dict = {"bar": {"i": 10}} assert to_dict(h) == h_dict assert from_dict(ForwardReferenceFoo, h_dict) == h