def test_type_with_unions(): class VariantA(NamedTuple): variant_a: int class VariantB(NamedTuple): variant_b: int variant_b_attr: int class X(NamedTuple): x: Union[None, VariantA, VariantB] y: Union[str, VariantA] mk_x, serialize_x = typeit.TypeConstructor(X) x = mk_x({'x': {'variant_a': 1}, 'y': 'y'}) assert isinstance(x.x, VariantA) data = {'x': {'variant_b': 1, 'variant_b_attr': 1}, 'y': 'y'} x = mk_x(data) assert isinstance(x.x, VariantB) assert data == serialize_x(x) assert mk_x({'x': None, 'y': 'y'}) == mk_x({'y': 'y'}) with pytest.raises(typeit.Error): # this is not the same as mk_x({}), # the empty structure is passed as attribute x, # which should match with only an empty named tuple definition, # which is not the same as None. mk_x({'x': {}})
def test_type_with_set(): class X(NamedTuple): a: FrozenSet b: FrozenSet[Any] c: frozenset d: FrozenSet[int] e: set f: Set g: Set[Any] h: Set[int] mk_x, serializer = typeit.TypeConstructor(X) x = mk_x({ 'a': [], 'b': [], 'c': [], 'd': [1], 'e': [], 'f': [], 'g': [], 'h': [1], }) assert x.a == x.b == x.c == frozenset() assert isinstance(x.d, frozenset) assert isinstance(x.e, set) assert x.h == {1} assert x.d == x.h
def test_iter_invalid_data(): class ItemType(Enum): ONE = 'one' TWO = 'two' class Item(NamedTuple): val: ItemType class X(NamedTuple): items: Sequence[Item] item: Item mk_x, serialize_x = typeit.TypeConstructor(X) data = { 'items': [ {'val': 'one'}, {'val': 'two'}, {'val': 'three'}, {'val': 'four'}, ] } try: x = mk_x(data) except typeit.Error as e: for inv in e: assert isinstance(inv, InvalidData)
def test_type_with_sequence(): class X(NamedTuple): x: int y: Sequence[Any] z: Sequence[str] mk_main, serializer = typeit.TypeConstructor(X) x = mk_main({'x': 1, 'y': [], 'z': ['Hello']}) assert x.y == [] assert x.z[0] == 'Hello'
def test_parse_sequence(): class X(NamedTuple): x: int y: Dict[str, Any] XS = Sequence[X] data = [{'x': 1, 'y': {}}] mk_xs, serialize_xs = typeit.TypeConstructor(XS) z = mk_xs(data) assert z[0].x == 1 assert serialize_xs(z) == data # Sequences with primitives XS = Sequence[int] data = [1, 2, 3] mk_xs, serialize_xs = typeit.TypeConstructor(XS) z = mk_xs(data) assert z[0] == 1 assert serialize_xs(z) == data
def test_type_with_empty_enum_variant(): class Types(Enum): A = '' B = 'b' class X(NamedTuple): x: int y: Types mk_x, serializer = typeit.TypeConstructor(X) for variant in Types: x = mk_x({'x': 1, 'y': variant.value}) assert x.y is variant with pytest.raises(typeit.Error): x = mk_x({'x': 1, 'y': None})
def test_enum_unions_serialization(): class E0(Enum): A = 'a' B = 'b' C = 'C' class E1(Enum): X = 'x' Y = 'y' Z = 'z' class MyType(NamedTuple): val: Union[E0, E1] __, serializer = typeit.TypeConstructor(MyType) assert serializer(MyType(val=E1.Z)) == {'val': 'z'}
def test_enum_like_types(): class Enums(Enum): A = 'a' B = 'b' class X(NamedTuple): e: Enums mk_x, serialize_x = typeit.TypeConstructor(X) data = {'e': 'a'} x = mk_x(data) assert isinstance(x.e, Enums) assert data == serialize_x(x) with pytest.raises(typeit.Error): x = mk_x({'e': None})
def test_type_with_dict(): """ Create a type with an explicit dictionary value that can hold any kv pairs """ class X(NamedTuple): x: int y: Dict[str, Any] mk_x, serializer = typeit.TypeConstructor(X) with pytest.raises(typeit.Error): mk_x({}) with pytest.raises(typeit.Error): mk_x({'x': 1}) x = mk_x({'x': 1, 'y': {'x': 1}}) assert x.x == x.y['x']
def test_type_with_tuple_primitives(): # There are several forms of tuple declarations # https://docs.python.org/3/library/typing.html#typing.Tuple # We want to support all possible fixed-length tuples, # including empty one class X(NamedTuple): a: Tuple[str, int] # fixed N-tuple b: Tuple # the following are equivalent c: tuple mk_x, serializer = typeit.TypeConstructor(X) x = mk_x({ 'a': ['value', 5], 'b': (), 'c': [], 'd': ['Hello', 'Random', 'Value', 5, None, True, {}], }) assert x.a == ('value', 5) assert x.b == () assert x.b == x.c with pytest.raises(typeit.Error): # 'abc' is not int x = mk_x({ 'a': ['value', 'abc'], 'b': [], 'c': [], }) with pytest.raises(typeit.Error): # .c field is required x = mk_x({ 'a': ['value', 5], 'b': [], }) with pytest.raises(typeit.Error): # .c field is required to be fixed sequence x = mk_x({ 'a': ['value', 'abc'], 'b': (), 'c': None, })
def test_parser_github_pull_request_payload(): data = GITHUB_PR_PAYLOAD_JSON github_pr_dict = json.loads(data) parsed, overrides = cg.parse_mapping(github_pr_dict) typ, overrides_ = cg.construct_type('main', parsed) overrides = overrides.update(overrides_) python_source, __ = cg.codegen_py(TypeitSchema(typ, overrides)) assert 'overrides' in python_source assert "PullRequest.links: '_links'," in python_source assert 'mk_main, serialize_main = TypeConstructor & overrides ^ Main' in python_source PullRequestType = get_type_hints(typ)['pull_request'] assert PullRequestType.links in overrides assert overrides[PullRequestType.links] == '_links' constructor, serializer = typeit.TypeConstructor(typ, overrides=overrides) github_pr = constructor(github_pr_dict) assert github_pr.pull_request.links.comments.href.startswith('http') assert github_pr_dict == serializer(github_pr)
def test_type_with_complex_tuples(): class Y(NamedTuple): a: Dict class X(NamedTuple): a: Tuple[Tuple[Dict, Y], int] b: Optional[Any] mk_x, serializer = typeit.TypeConstructor(X) x = mk_x({ 'a': [[{}, { 'a': { 'inner': 'value' } }], 5], }) assert isinstance(x.a[0][1], Y) assert isinstance(x.a[1], int) assert x.b is None x = mk_x({'a': [[{}, {'a': {'inner': 'value'}}], 5], 'b': Y(a={})}) assert isinstance(x.b, Y)
def test_parse_builtins(typ, data): mk_x, serialize_x = typeit.TypeConstructor(typ) z = mk_x(data) assert z == data assert serialize_x(z) == data
def test_unsupported_variable_length_tuples(): class X(NamedTuple): a: Tuple[int, ...] with pytest.raises(TypeError): mk_x, serialize_x = typeit.TypeConstructor(X)