def test_only_union(): from attrkid import to_dict from attrkid.fields import ( list_field, object_field, string_field, ) from attrkid.kind import UnionKind @attr.s class Value: value = string_field() @attr.s class And: items = list_field(Value, is_only_field=True) @attr.s class Not: item = object_field(Value, is_only_field=True) @attr.s class Container: expr = object_field(UnionKind(('and', And), ('not', Not))) c1 = Container(expr=And(items=[Value('hello'), Value('world')])) c2 = Container(expr=Not(item=Value('boo'))) expected_c1 = {'expr': {'and': [{'value': 'hello'}, {'value': 'world'}]}} assert expected_c1 == to_dict(c1) expected_c2 = {'expr': {'not': {'value': 'boo'}}} assert expected_c2 == to_dict(c2)
def test_only_field(): from attrkid import from_dict, to_dict from attrkid.fields import ( list_field, object_field, string_field, ) @attr.s class Value: value = string_field() @attr.s class Not: item = object_field(Value, is_only_field=True) @attr.s class And: items = list_field(Value, is_only_field=True) n = Not(item=Value(value='boo')) expected_n = {'value': 'boo'} assert expected_n == to_dict(n) assert n == from_dict(Not, expected_n) a = And(items=[Value(value='hello'), Value(value='world')]) expected_a = [{'value': 'hello'}, {'value': 'world'}] assert expected_a == to_dict(a) assert a == from_dict(And, expected_a)
def test_to_dict(dt): from attrkid import to_dict, from_dict from attrkid.fields import datetime_field, list_field from attrkid.options import SerdeOptions @attr.s class X: f = attr.ib() @attr.s class M: s = attr.ib(validator=instance_of(str)) i = attr.ib(validator=instance_of(int)) nullable = attr.ib() d = datetime_field() lst1 = list_field(int) lst2 = list_field(X) m = M( s='hello', i=42, d=dt, lst1=[1, 2, 3], lst2=[X(f=1)], nullable=None, ) options = SerdeOptions(omit_null_values=False) actual = to_dict(m, options=options) expected = { 's': 'hello', 'i': 42, 'd': '2017-11-13T15:12:00.000000Z', 'lst1': [1, 2, 3], 'lst2': [{ 'f': 1 }], 'nullable': None } assert expected == actual expected_nullable = { 's': 'hello', 'i': 42, 'd': '2017-11-13T15:12:00.000000Z', 'lst1': [1, 2, 3], 'lst2': [{ 'f': 1 }], } actual_nullable = to_dict(m) assert expected_nullable == actual_nullable # Check we can round-trip m1 = from_dict(M, actual) assert m == m1 m2 = from_dict(M, actual_nullable) assert m == m2
def test_datetime_custom_serialisation(dt): from attrkid import to_dict from attrkid.fields import datetime_field from attrkid.options import SerdeOptions @attr.s class M: f = datetime_field() m = M(f=dt) assert {'f': '2017-11-13T15:12:00.000000Z'} == to_dict(m) options = SerdeOptions(datetime_format='%Y') assert {'f': '2017'} == to_dict(m, options=options)
def test_tuple_of_union(): from attrkid import to_dict, from_dict from attrkid.fields import string_field, tuple_field from attrkid.kind import UnionKind @attr.s(hash=True) class A: f = string_field() @attr.s(hash=True) class B: f = string_field() @attr.s(hash=True) class C: t = tuple_field(UnionKind( ('a', A), ('b', B), )) # Check we can round-trip a = A(f='a') b = B(f='b') c = C(t=(a, b)) as_dict = to_dict(c) as_ob = from_dict(C, as_dict) assert c == as_ob
def test_default_from_attr(): from attrkid import from_dict, to_dict from attrkid.fields import string_field, set_field @attr.s class M: f = string_field(default_from_attr='_f') s = set_field(str, default_from_attr='_s') @property def _f(self): return 'hello' @property def _s(self): return {'hey'} m = M() assert 'hello' == m.f assert {'hey'} == m.s expected = {'f': 'hello', 's': ['hey']} assert expected == to_dict(m) # default_from_attr only provides defaults inp = {'f': 'something', 's': ['different']} actual = from_dict(M, inp) assert M(f='something', s={'different'}) == actual
def test_union_field(): from attrkid import to_dict, from_dict from attrkid.fields import ( bool_field, list_field, object_field, ) from attrkid.kind import UnionKind @attr.s class Value: value = bool_field() @attr.s class And: items = list_field(Value) @attr.s class Not: item = object_field(Value) @attr.s class Container: item = object_field(UnionKind(('and', And), ('not', Not))) c1 = Container(item=And(items=[Value(value=True), Value(value=False)])) expected_c1 = { 'item': { 'and': { 'items': [{ 'value': True }, { 'value': False }], } } } actual_c1 = to_dict(c1) assert expected_c1 == actual_c1 c2 = Container(item=Not(item=Value(value=True))) expected_c2 = {'item': {'not': {'item': {'value': True}}}} actual_c2 = to_dict(c2) assert expected_c2 == actual_c2 assert c1 == from_dict(Container, actual_c1) assert c2 == from_dict(Container, actual_c2)
def test_int_field(): from attrkid import from_dict, to_dict from attrkid.fields import int_field @attr.s class M: n = int_field() x = M(n=5) assert x == from_dict(M, to_dict(x))
def test_tuple_field_self(): from attrkid import from_dict, to_dict from attrkid.fields import tuple_field from attrkid.constants import SELF @attr.s class MTuple: as_tuple = tuple_field(SELF) m5 = MTuple() m6 = MTuple(as_tuple=(m5, )) assert m6 == from_dict(MTuple, to_dict(m6))
def test_list_fields_self(): from attrkid import to_dict, from_dict from attrkid.fields import list_field from attrkid.constants import SELF @attr.s class MList: as_list = list_field(SELF) m1 = MList() m2 = MList(as_list=[m1], ) assert m2 == from_dict(MList, to_dict(m2))
def test_decimal_field(): from attrkid import to_dict, from_dict from attrkid.fields import decimal_field @attr.s class M: f = decimal_field(prec=5) m = M(f=decimal.Decimal('3.4')) as_dict = to_dict(m) expected = {'f': '3.4'} assert expected == as_dict as_ob = from_dict(M, as_dict) assert m == as_ob
def test_non_serialisable(): from attrkid import from_dict, to_dict from attrkid.fields import string_field @attr.s class M: a = string_field() b = string_field(should_serialise=False) m = M(a='a', b='b') expected = {'a': 'a'} assert expected == to_dict(m) m2 = from_dict(M, expected) assert 'a' == m2.a assert m2.b is None
def test_set_field_object(): from attrkid import from_dict, to_dict from attrkid.fields import set_field, int_field @attr.s(slots=True, frozen=True, hash=True) class A: f = int_field() @attr.s class M: xs = set_field(A) m = M(xs={A(f=3)}) d = to_dict(m) assert m == from_dict(M, d)
def test_set_field_self(): from attrkid import from_dict, to_dict from attrkid.fields import set_field from attrkid.constants import SELF @attr.s(hash=True) class MSet: as_set = set_field(SELF) m3 = MSet() m4 = MSet(as_set={m3}) expected_dict = {'as_set': [{'as_set': []}]} as_dict = to_dict(m4) assert expected_dict == as_dict assert m4 == from_dict(MSet, as_dict)
def test_object_field(): from attrkid import to_dict, from_dict from attrkid.fields import object_field @attr.s class X: f = attr.ib() @attr.s class M: f = object_field(X) m = M(f=X(f=1)) expected = {'f': {'f': 1}} assert expected == to_dict(m) loaded = from_dict(M, expected) assert m == loaded
def test_object_field_self(): from attrkid import to_dict, from_dict from attrkid.constants import SELF from attrkid.fields import object_field from attrkid.options import SerdeOptions @attr.s class M: f = object_field(SELF, is_optional=True) m = M(f=M(f=None)) expected = {'f': {}} options = SerdeOptions(omit_null_values=True) assert expected == to_dict(m, options=options) loaded = from_dict(M, expected) assert m == loaded
def test_optional_tuple_field(): from attrkid import to_dict, from_dict from attrkid.fields import tuple_field @attr.s class X: pass @attr.s class M: f = tuple_field(X, is_optional=True, default=None) m = M() n = M(f=None) assert m == n as_dict = to_dict(m) assert m == from_dict(M, as_dict) data = {} assert m == from_dict(M, data)
def test_list_of_union_kind(): from attrkid import to_dict from attrkid.fields import list_field, string_field from attrkid.kind import UnionKind @attr.s class V1: a = string_field() @attr.s class V2: b = string_field() @attr.s class M: v = list_field(UnionKind(('v1', V1), ('v2', V2)), is_only_field=True) m = M(v=[V1(a='hello'), V2(b='world')]) actual = to_dict(m) expected = [{'v1': {'a': 'hello'}}, {'v2': {'b': 'world'}}] assert expected == actual
def test_to_dict_nested_flags(dt): """ When a sub-model has a datetime, and to_dict is called with convert_datetimes=False, make sure that the datetimes are indeed left alone. Same for None/null values - `convert_datetimes` and `omit_null_values` should be propagated. """ from attrkid import to_dict from attrkid.fields import object_field, datetime_field from attrkid.constants import SELF from attrkid.options import SerdeOptions @attr.s class M: dt = datetime_field() sub = object_field(SELF, is_optional=True, default=None) m = M(dt=dt, sub=M(dt=dt)) expected = {'dt': dt, 'sub': {'dt': dt, 'sub': None}} options = SerdeOptions(convert_datetimes=False, omit_null_values=False) actual = to_dict(m, options=options) assert expected == actual
def test_union_field_deferred(mocker): from attrkid import from_dict, to_dict from attrkid.fields import object_field from attrkid.kind import DeferredKind, UnionKind import_module = mocker.patch('importlib.import_module') import_module.return_value = sys.modules[__name__] selector_for = mocker.patch('attrkid.kind.UnionKind.selector_for') selector_for.return_value = 'target' @attr.s class M: m = object_field( UnionKind( ('target', DeferredKind('tests.test_models.TargetModel')), )) m = M(m=TargetModel()) import_module.assert_called_with('tests.test_models') as_dict = to_dict(m) selector_for.assert_called_with(TargetModel) assert m == from_dict(M, as_dict)