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)) @dataclass class C(object): a: 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._union_registry[Union[cl_a, cl_b]] = handler assert inst == converter.structure(converter.unstructure(inst), C) del converter._union_registry[Union[cl_a, cl_b]]
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_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_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_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_simple_roundtrip_defaults(cls_and_vals, strat): """ Simple classes with metadata can be unstructured and restructured. """ a, _ = cls_and_vals cl = make_dataclass("HypClass", [("a", a.type, a)]) converter = Converter(unstruct_strat=strat) inst = cl() assert inst == converter.structure(converter.unstructure(inst), cl)
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_simple_name_modifiers(cls_and_vals): """ Simple classes with metadata can be unstructured and restructured. """ a, vals = cls_and_vals converter = Converter() if len(fields(a)) > 0: fld = mod.name("t-t", fields(a)[0]) cl = make_dataclass("HypClass", [("t_t", fld.type, fld)]) inst = cl(vals[0]) assert converter.unstructure(inst).get("t-t", MISSING) is not MISSING else: cl = make_dataclass("HypClass", []) inst = cl() assert inst == converter.structure(converter.unstructure(inst), cl)
def test_individual_overrides(cl_and_vals): """ Test omitting default values on a per-class basis, but with individual overrides. """ converter = Converter() cl, vals = cl_and_vals for attr, val in zip( tuple(v for _, v in cl.__dataclass_fields__.items()), vals ): if attr.default is not MISSING: break else: assume(False) chosen = attr converter.register_unstructure_hook( cl, make_dict_unstructure_fn( cl, converter, omit_if_default=True, **{attr.name: override(omit_if_default=False)} ), ) inst = cl(*vals) res = converter.unstructure(inst) for attr, val in zip( tuple(v for _, v in cl.__dataclass_fields__.items()), vals ): if attr is chosen: assert attr.name in res elif attr.default is not MISSING: if val == attr.default: assert attr.name not in res else: assert attr.name in res elif attr.default_factory is not MISSING: if val == attr.default_factory(): assert attr.name not in res else: assert attr.name in res
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_optional_field_roundtrip(cl_and_vals): """ Classes with optional fields can be unstructured and structured. """ converter = Converter() cl, vals = cl_and_vals @dataclass class C(object): a: 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 converter(): return Converter()