def test_structuring_unsupported(): """Loading unsupported classes should throw.""" converter = Converter() with raises(ValueError): converter.structure(1, Converter) with raises(ValueError): converter.structure(1, Union[int, str])
def test_structuring_optional_primitives(primitive_and_type): """Test structuring Optional primitive types.""" converter = Converter() val, type = primitive_and_type assert converter.structure(val, Optional[type]) == val assert converter.structure(None, Optional[type]) is None
def test_structuring_lists_of_opt(list_and_type): """Test structuring lists of Optional primitive types.""" converter = Converter() l, t = list_and_type l.append(None) args = t.__args__ is_optional = args[0] is Optional or (is_union_type(args[0]) and len(args[0].__args__) == 2 and args[0].__args__[1] is NoneType) if not is_bare(t) and (args[0] not in (Any, str) and not is_optional): with raises((TypeError, ValueError)): converter.structure(l, t) optional_t = Optional[args[0]] # We want to create a generic type annotation with an optional # type parameter. t = change_type_param(t, optional_t) converted = converter.structure(l, t) for x, y in zip(l, converted): assert x == y t.__args__ = args
def test_union_field_roundtrip(cl_and_vals_a, cl_and_vals_b, strat): """ Classes with union fields can be unstructured and structured. """ converter = Converter(unstruct_strat=strat) cl_a, vals_a = cl_and_vals_a cl_b, vals_b = cl_and_vals_b a_field_names = {a.name for a in fields(cl_a)} b_field_names = {a.name for a in fields(cl_b)} assume(a_field_names) assume(b_field_names) common_names = a_field_names & b_field_names assume(len(a_field_names) > len(common_names)) @attr.s class C(object): a = attr.ib(type=Union[cl_a, cl_b]) inst = C(a=cl_a(*vals_a)) if strat is UnstructureStrategy.AS_DICT: assert inst == converter.structure(converter.unstructure(inst), C) else: # Our disambiguation functions only support dictionaries for now. with pytest.raises(ValueError): converter.structure(converter.unstructure(inst), C) def handler(obj, _): return converter.structure(obj, cl_a) converter.register_structure_hook(Union[cl_a, cl_b], handler) assert inst == converter.structure(converter.unstructure(inst), C)
def test_simple_roundtrip_defaults(cls_and_vals, strat): """ Simple classes with metadata can be unstructured and restructured. """ a, _ = cls_and_vals cl = make_class("HypClass", {"a": a}) converter = Converter(unstruct_strat=strat) inst = cl() assert converter.unstructure(converter.structure( {}, cl)) == converter.unstructure(inst) assert inst == converter.structure(converter.unstructure(inst), cl)
def test_structuring_unsupported(): """Loading unsupported classes should throw.""" converter = Converter() with raises(StructureHandlerNotFoundError) as exc: converter.structure(1, Converter) assert exc.value.type_ is Converter with raises(StructureHandlerNotFoundError) as exc: converter.structure(1, Union[int, str]) assert exc.value.type_ is Union[int, str]
def test_structuring_seqs(seq_and_type): """Test structuring sequence generic types.""" converter = Converter() iterable, t = seq_and_type converted = converter.structure(iterable, t) for x, y in zip(iterable, converted): assert x == y
def test_renaming(cl_and_vals, data): converter = Converter() cl, vals = cl_and_vals attrs = fields(cl) to_replace = data.draw(sampled_from(attrs)) u_fn = make_dict_unstructure_fn( cl, converter, **{to_replace.name: override(rename="class")} ) s_fn = make_dict_structure_fn( cl, converter, **{to_replace.name: override(rename="class")} ) converter.register_structure_hook(cl, s_fn) converter.register_unstructure_hook(cl, u_fn) inst = cl(*vals) raw = converter.unstructure(inst) assert "class" in raw new_inst = converter.structure(raw, cl) assert inst == new_inst
def test_simple_roundtrip(cls_and_vals, strat): """ Simple classes with metadata can be unstructured and restructured. """ converter = Converter(unstruct_strat=strat) cl, vals = cls_and_vals inst = cl(*vals) assert inst == converter.structure(converter.unstructure(inst), cl)
def test_stringifying_dicts(dict_and_type): converter = Converter() d, t = dict_and_type converted = converter.structure(d, Dict[str, str]) for k, v in d.items(): assert converted[str(k)] == str(v)
def test_structuring_dicts(dict_and_type): converter = Converter() d, t = dict_and_type converted = converter.structure(d, t) assert converted == d assert converted is not d
def test_nested_roundtrip(cls_and_vals, strat): """ Nested classes with metadata can be unstructured and restructured. """ converter = Converter(unstruct_strat=strat) cl, vals = cls_and_vals # Vals are a tuple, convert into a dictionary. inst = cl(*vals) assert inst == converter.structure(converter.unstructure(inst), cl)
def test_optional_field_roundtrip(cl_and_vals): """ Classes with optional fields can be unstructured and structured. """ converter = Converter() cl, vals = cl_and_vals @attr.s class C(object): a = attr.ib(type=Optional[cl]) inst = C(a=cl(*vals)) assert inst == converter.structure(converter.unstructure(inst), C) inst = C(a=None) unstructured = converter.unstructure(inst) assert inst == converter.structure(unstructured, C)
def test_310_optional_field_roundtrip(cl_and_vals): """ Classes with optional fields can be unstructured and structured. """ converter = Converter() cl, vals = cl_and_vals @define class C: a: cl | None inst = C(a=cl(*vals)) assert inst == converter.structure(converter.unstructure(inst), C) inst = C(a=None) unstructured = converter.unstructure(inst) assert inst == converter.structure(unstructured, C)
def test_structuring_sets(set_and_type, set_type): """Test structuring generic sets.""" converter = Converter() set_, input_set_type = set_and_type if input_set_type not in (Set, FrozenSet, MutableSet): set_type = set_type[input_set_type.__args__[0]] converted = converter.structure(set_, set_type) assert converted == set_ # Set[int] can't be used with isinstance any more. non_generic = (set_type.__origin__ if set_type.__origin__ is not None else set_type) assert isinstance(converted, non_generic) converted = converter.structure(set_, Any) assert converted == set_ assert isinstance(converted, type(set_))
def test_subclass_registration_is_honored(): """If a subclass is registered after a superclass, that subclass handler should be dispatched for structure """ converter = Converter() class Foo(object): def __init__(self, value): self.value = value class Bar(Foo): pass converter.register_structure_hook(Foo, lambda obj, cls: cls("foo")) assert converter.structure(None, Foo).value == "foo" assert converter.structure(None, Bar).value == "foo" converter.register_structure_hook(Bar, lambda obj, cls: cls("bar")) assert converter.structure(None, Foo).value == "foo" assert converter.structure(None, Bar).value == "bar"
def test_structuring_dicts_opts(dict_and_type, data): """Structure dicts, but with optional primitives.""" converter = Converter() d, t = dict_and_type assume(not is_bare(t)) t.__args__ = (t.__args__[0], Optional[t.__args__[1]]) d = {k: v if data.draw(booleans()) else None for k, v in d.items()} converted = converter.structure(d, t) assert converted == d assert converted is not d
def test_structure_hook_func(): """ testing the hook_func method """ converter = Converter() def can_handle(cls): return cls.__name__.startswith("F") def handle(obj, cls): return "hi" class Foo(object): pass class Bar(object): pass converter.register_structure_hook_func(can_handle, handle) assert converter.structure(10, Foo) == "hi" with raises(ValueError): converter.structure(10, Bar)
def deserialize(*, data: str, structure: TStructure, converter: cattr.Converter) -> TDeserializeReturn: """Translate API data into models.""" try: data = json.loads(data) except json.JSONDecodeError as ex: raise DeserializeError(f"Bad json {ex}") try: response: TDeserializeReturn = converter.structure( # type: ignore data, structure) except (TypeError, AttributeError) as ex: raise DeserializeError(f"Bad data {ex}") return response
def test_stringifying_sets(set_and_type): """Test structuring generic sets and converting the contents to str.""" converter = Converter() set_, input_set_type = set_and_type if is_bare(input_set_type): input_set_type = input_set_type[str] else: input_set_type.__args__ = (str, ) converted = converter.structure(set_, input_set_type) assert len(converted) == len(set_) for e in set_: assert str(e) in converted
def test_stringifying_lists_of_opt(list_and_type): """Test structuring Optional primitive types into strings.""" converter = Converter() l, t = list_and_type l.append(None) converted = converter.structure(l, List[Optional[str]]) for x, y in zip(l, converted): if x is None: assert x is y else: assert str(x) == y
def test_unmodified_generated_structuring(cl_and_vals): converter = Converter() cl, vals = cl_and_vals fn = make_dict_structure_fn(cl, converter) inst = cl(*vals) unstructured = converter.unstructure(inst) converter.register_structure_hook(cl, fn) res = converter.structure(unstructured, cl) assert inst == res
def test_structuring_hetero_tuples(list_of_vals_and_types): """Test structuring heterogenous tuples.""" converter = Converter() types = tuple(e[1] for e in list_of_vals_and_types) vals = [e[0] for e in list_of_vals_and_types] t = Tuple[types] converted = converter.structure(vals, t) assert isinstance(converted, tuple) for x, y in zip(vals, converted): assert x == y for x, y in zip(types, converted): assert isinstance(y, x)
def test_stringifying_tuples(list_of_vals_and_types): """Stringify all elements of a heterogeneous tuple.""" converter = Converter() vals = [e[0] for e in list_of_vals_and_types] t = Tuple[(str, ) * len(list_of_vals_and_types)] converted = converter.structure(vals, t) assert isinstance(converted, tuple) for x, y in zip(vals, converted): assert str(x) == y for x in converted: # this should just be unicode, but in python2, '' is not unicode assert isinstance(x, str)
def test_structuring_primitive_union_hook(ints): """Registering a union loading hook works.""" converter = Converter() def structure_hook(val, cl): """Even ints are passed through, odd are stringified.""" return val if val % 2 == 0 else str(val) converter.register_structure_hook(Union[str, int], structure_hook) converted = converter.structure(ints, List[Union[str, int]]) for x, y in zip(ints, converted): if x % 2 == 0: assert x == y else: assert str(x) == y
def test_structure_union_edge_case(): converter = Converter() @attr.s(auto_attribs=True) class A: a1: Any a2: Optional[Any] = None @attr.s(auto_attribs=True) class B: b1: Any b2: Optional[Any] = None assert converter.structure([{ "a1": "foo" }, { "b1": "bar" }], List[Union[A, B]]) == [A("foo"), B("bar")]
def test_able_to_structure_generics(converter: Converter, t, t2, result): res = converter.structure(asdict(result), TClass[t, t2]) assert res == result
def test_structuring_enums(data, enum): """Test structuring enums by their values.""" converter = Converter() val = data.draw(sampled_from(list(enum))) assert converter.structure(val.value, enum) == val
def test_structuring_primitives(primitive_and_type): """Test just structuring a primitive value.""" converter = Converter() val, t = primitive_and_type assert converter.structure(val, t) == val assert converter.structure(val, Any) == val