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, _ = 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: unstructured = converter.unstructure(inst) assert inst == converter.structure( converter.unstructure(unstructured), 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) unstructured = converter.unstructure(inst) assert inst == converter.structure(unstructured, C)
def test_type_overrides(cl_and_vals): """ Type overrides on the GenConverter work. """ converter = Converter(type_overrides={int: override(omit_if_default=True)}) cl, vals = cl_and_vals inst = cl(*vals) unstructured = converter.unstructure(inst) for field, val in zip(fields(cl), vals): if field.type is int: if field.default is not None: if isinstance(field.default, Factory): if not field.default.takes_self and field.default() == val: assert field.name not in unstructured elif field.default == val: assert field.name not in unstructured
def test_seq_of_simple_classes_unstructure(cls_and_vals, seq_type_and_annotation): """Dumping a sequence of primitives is a simple copy operation.""" converter = Converter() test_val = ("test", 1) for cl, _ in cls_and_vals: converter.register_unstructure_hook(cl, lambda _: test_val) break # Just register the first class. seq_type, annotation = seq_type_and_annotation inputs = seq_type(cl(*vals) for cl, vals in cls_and_vals) outputs = converter.unstructure( inputs, unstructure_as=annotation[cl] if annotation not in (Tuple, tuple) else annotation[cl, ...], ) assert all(e == test_val for e in outputs)
def test_overriding_generated_structure(): """Test overriding a generated structure hook works.""" converter = Converter() @attr.define class Inner: a: int @attr.define class Outer: i: Inner inst = Outer(Inner(1)) raw = converter.unstructure(inst) converter.structure(raw, Outer) converter.register_structure_hook(Inner, lambda p, _: Inner(p["a"] + 1)) r = converter.structure(raw, Outer) assert r.i.a == 2
def _unstructure(self, converter: GenConverter) -> dict[str, Any]: # omit glyph name attribute, already used as key glyphs: dict[str, dict[str, Any]] = {} for glyph_name in self._glyphs: g = converter.unstructure(self[glyph_name]) assert glyph_name == g.pop("name") glyphs[glyph_name] = g d: dict[str, Any] = { # never omit name even if == 'public.default' as that acts as # the layer's "key" in the layerSet. "name": self._name, } default: Any for key, value, default in [ ("default", self._default, self._name == DEFAULT_LAYER_NAME), ("glyphs", glyphs, {}), ("lib", self._lib, {}), ]: if not converter.omit_if_default or value != default: d[key] = value if self.color is not None: d["color"] = self.color return d
def test_unstructure_generic_attrs(): c = GenConverter() @attrs(auto_attribs=True) class Inner(Generic[T]): a: T @attrs(auto_attribs=True) class Outer: inner: Inner[int] initial = Outer(Inner(1)) raw = c.unstructure(initial) assert raw == {"inner": {"a": 1}} new = c.structure(raw, Outer) assert initial == new @attrs(auto_attribs=True) class OuterStr: inner: Inner[str] assert c.structure(raw, OuterStr) == OuterStr(Inner("1"))
def _unstructure(self, converter: GenConverter) -> list[dict[str, Any]]: return [converter.unstructure(layer) for layer in self]
def test_collection_unstructure_override_seq(): """Test overriding unstructuring seq.""" # First approach, predicate hook c = GenConverter() c._unstructure_func.register_func_list([( is_sequence, partial(c.gen_unstructure_iterable, unstructure_to=tuple), True, )]) assert c.unstructure([1, 2, 3], unstructure_as=Sequence[int]) == (1, 2, 3) @attr.define class MyList: args = attr.ib(converter=list) # Second approach, using abc.MutableSequence c = GenConverter(unstruct_collection_overrides={MutableSequence: MyList}) assert c.unstructure([1, 2, 3], unstructure_as=Sequence[int]) == [1, 2, 3] assert c.unstructure([1, 2, 3], unstructure_as=MutableSequence[int]) == MyList([ 1, 2, 3, ]) assert c.unstructure([1, 2, 3]) == MyList([ 1, 2, 3, ]) assert c.unstructure((1, 2, 3)) == [ 1, 2, 3, ] # Second approach, using abc.Sequence c = GenConverter(unstruct_collection_overrides={Sequence: MyList}) assert c.unstructure([1, 2, 3], unstructure_as=Sequence[int]) == MyList([1, 2, 3]) assert c.unstructure( [1, 2, 3], unstructure_as=MutableSequence[int]) == MyList([1, 2, 3]) assert c.unstructure([1, 2, 3]) == MyList([1, 2, 3]) assert c.unstructure((1, 2, 3), unstructure_as=tuple[int, ...]) == MyList([ 1, 2, 3, ]) # Second approach, using __builtins__.list c = GenConverter(unstruct_collection_overrides={list: MyList}) assert c.unstructure([1, 2, 3], unstructure_as=Sequence[int]) == [1, 2, 3] assert c.unstructure([1, 2, 3], unstructure_as=MutableSequence[int]) == [ 1, 2, 3, ] assert c.unstructure([1, 2, 3]) == MyList([ 1, 2, 3, ]) assert c.unstructure((1, 2, 3)) == [ 1, 2, 3, ] # Second approach, using __builtins__.tuple c = GenConverter(unstruct_collection_overrides={tuple: MyList}) assert c.unstructure([1, 2, 3], unstructure_as=Sequence[int]) == [1, 2, 3] assert c.unstructure([1, 2, 3], unstructure_as=MutableSequence[int]) == [ 1, 2, 3, ] assert c.unstructure([1, 2, 3]) == [ 1, 2, 3, ] assert c.unstructure((1, 2, 3)) == MyList([ 1, 2, 3, ])
def test_collection_unstructure_override_mapping(): """Test overriding unstructuring mappings.""" # Using Counter c = GenConverter(unstruct_collection_overrides={Counter: Map}) assert c.unstructure(Counter({1: 2})) == Map({1: 2}) assert c.unstructure(Counter({1: 2}), unstructure_as=Counter[int]) == Map({1: 2}) assert c.unstructure({1: 2}) == {1: 2} assert c.unstructure({1: 2}, unstructure_as=MutableMapping[int, int]) == { 1: 2 } assert c.unstructure({1: 2}, unstructure_as=Mapping[int, int]) == {1: 2} # Using __builtins__.dict c = GenConverter(unstruct_collection_overrides={dict: Map}) assert c.unstructure(Counter({1: 2})) == Map({1: 2}) assert c.unstructure(Counter({1: 2}), unstructure_as=Counter[int]) == Map({1: 2}) assert c.unstructure({1: 2}) == Map({1: 2}) assert c.unstructure({1: 2}, unstructure_as=MutableMapping[int, int]) == { 1: 2 } assert c.unstructure({1: 2}, unstructure_as=Mapping[int, int]) == {1: 2} # Using MutableMapping c = GenConverter(unstruct_collection_overrides={MutableMapping: Map}) assert c.unstructure(Counter({1: 2})) == Map({1: 2}) assert c.unstructure(Counter({1: 2}), unstructure_as=Counter[int]) == Map({1: 2}) assert c.unstructure({1: 2}) == Map({1: 2}) assert c.unstructure({1: 2}, unstructure_as=MutableMapping[int, int]) == Map({1: 2}) assert c.unstructure({1: 2}, unstructure_as=Mapping[int, int]) == {1: 2} # Using Mapping c = GenConverter(unstruct_collection_overrides={Mapping: Map}) assert c.unstructure(Counter({1: 2})) == Map({1: 2}) assert c.unstructure(Counter({1: 2}), unstructure_as=Counter[int]) == Map({1: 2}) assert c.unstructure({1: 2}) == Map({1: 2}) assert c.unstructure({1: 2}, unstructure_as=MutableMapping[int, int]) == Map({1: 2}) assert c.unstructure({1: 2}, unstructure_as=Mapping[int, int]) == Map({1: 2})