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_unmodified_generated_unstructuring(cl_and_vals): converter = Converter() cl, vals = cl_and_vals fn = make_dict_unstructure_fn(cl, converter) inst = cl(*vals) res_expected = converter.unstructure(inst) converter.register_unstructure_hook(cl, fn) res_actual = converter.unstructure(inst) assert res_expected == res_actual
def test_nodefs_generated_unstructuring(cl_and_vals): """Test omitting default values on a per-attribute basis.""" converter = Converter() cl, vals = cl_and_vals attr_is_default = False for attr, val in zip(cl.__attrs_attrs__, vals): if attr.default is not NOTHING: fn = make_dict_unstructure_fn( cl, converter, **{attr.name: override(omit_if_default=True)} ) if attr.default == val: attr_is_default = True break else: assume(False) converter.register_unstructure_hook(cl, fn) inst = cl(*vals) res = converter.unstructure(inst) if attr_is_default: assert attr.name not in res
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_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_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_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_omitting(): converter = Converter() @define class A: a: int b: int = field(init=False) converter.register_unstructure_hook( A, make_dict_unstructure_fn(A, converter, b=override(omit=True))) assert converter.unstructure(A(1)) == {"a": 1}
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(adapted_fields(cl), vals): if attr.default is not NOTHING: break else: assume(False) chosen_name = attr.name 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) assert "Hyp" not in repr(res) assert "Factory" not in repr(res) for attr, val in zip(adapted_fields(cl), vals): if attr.name == chosen_name: assert attr.name in res elif attr.default is not NOTHING: if not isinstance(attr.default, Factory): if val == attr.default: assert attr.name not in res else: assert attr.name in res else: if attr.default.takes_self: if val == attr.default.factory(inst): assert attr.name not in res else: assert attr.name in res else: if val == attr.default.factory(): assert attr.name not in res else: assert attr.name in res
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_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(cl.__attrs_attrs__, vals): if attr.default is not NOTHING: 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(cl.__attrs_attrs__, vals): if attr is chosen: assert attr.name in res elif attr.default is not NOTHING: if not isinstance(attr.default, Factory): if val == attr.default: assert attr.name not in res else: assert attr.name in res else: if val == attr.default.factory(): assert attr.name not in res else: assert attr.name in res
def test_nodefs_generated_unstructuring_cl(cl_and_vals): """Test omitting default values on a per-class basis.""" converter = Converter() cl, vals = cl_and_vals for attr, val in zip(cl.__attrs_attrs__, vals): if attr.default is not NOTHING: break else: assume(False) converter.register_unstructure_hook( cl, make_dict_unstructure_fn(cl, converter, omit_if_default=True) ) inst = cl(*vals) res = converter.unstructure(inst) for attr, val in zip(cl.__attrs_attrs__, vals): if attr.default is not NOTHING: if not isinstance(attr.default, Factory): if val == attr.default: assert attr.name not in res else: assert attr.name in res else: # The default is a factory, but might take self. if attr.default.takes_self: if val == attr.default.factory(cl): assert attr.name not in res else: assert attr.name in res else: if val == attr.default.factory(): assert attr.name not in res else: assert attr.name in res