Beispiel #1
0
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
Beispiel #2
0
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
Beispiel #3
0
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
Beispiel #4
0
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)
Beispiel #5
0
def test_calling_back():
    """Calling unstructure_attrs_asdict from a hook should not override a manual hook."""
    converter = Converter()

    @attr.define
    class C:
        a: int = attr.ib(default=1)

    def handler(obj):
        return {
            "type_tag": obj.__class__.__name__,
            **converter.unstructure_attrs_asdict(obj),
        }

    converter.register_unstructure_hook(C, handler)

    inst = C()

    expected = {"type_tag": "C", "a": 1}

    unstructured1 = converter.unstructure(inst)
    unstructured2 = converter.unstructure(inst)

    assert unstructured1 == expected, repr(unstructured1)
    assert unstructured2 == unstructured1, repr(unstructured2)
Beispiel #6
0
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])
Beispiel #7
0
def test_forbid_extra_keys_nested_override():
    @attr.s
    class C:
        a = attr.ib(type=int, default=1)

    @attr.s
    class A:
        c = attr.ib(type=C)
        a = attr.ib(type=int, default=2)

    converter = Converter(forbid_extra_keys=True)
    unstructured = {"a": 3, "c": {"a": 4}}
    # at this point, structuring should still work
    converter.structure(unstructured, A)
    # if we break it in the subclass, we need it to raise
    unstructured["c"]["aa"] = 5
    with pytest.raises(Exception):
        converter.structure(unstructured, A)
    # we can "fix" that by disabling forbid_extra_keys on the subclass
    hook = make_dict_structure_fn(C, converter, _cattr_forbid_extra_keys=False)
    converter.register_structure_hook(C, hook)
    converter.structure(unstructured, A)
    # but we should still raise at the top level
    unstructured["b"] = 6
    with pytest.raises(Exception):
        converter.structure(unstructured, A)
Beispiel #8
0
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
Beispiel #9
0
def test_annotated_attrs():
    """Annotation support works for attrs classes."""
    from typing import Annotated

    converter = Converter()

    @attr.define
    class Inner:
        a: int

    @attr.define
    class Outer:
        i: Annotated[Inner, "test"]
        j: list[Annotated[Inner, "test"]]

    orig = Outer(Inner(1), [Inner(1)])
    raw = converter.unstructure(orig)

    assert raw == {"i": {"a": 1}, "j": [{"a": 1}]}

    structured = converter.structure(raw, Outer)
    assert structured == orig

    # Now register a hook and rerun the test.
    converter.register_unstructure_hook(Inner, lambda v: {"a": 2})

    raw = converter.unstructure(Outer(Inner(1), [Inner(1)]))

    assert raw == {"i": {"a": 2}, "j": [{"a": 2}]}

    structured = converter.structure(raw, Outer)
    assert structured == Outer(Inner(2), [Inner(2)])
Beispiel #10
0
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
Beispiel #11
0
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)
Beispiel #12
0
def init_converter():
    converter = Converter()
    # converter.register_unstructure_hook(pendulum.DateTime, lambda dt: dt.to_iso8601_string())
    # converter.register_structure_hook(pendulum.DateTime, lambda ts, _: pendulum.parse(ts))
    converter.register_unstructure_hook(datetime,
                                        lambda dt: dt.isoformat() + 'Z')
    converter.register_structure_hook(datetime, lambda ts, _: parser.parse(ts))
    return converter
Beispiel #13
0
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
Beispiel #14
0
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)
Beispiel #15
0
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)
Beispiel #16
0
def test_simple_roundtrip_with_extra_keys_forbidden(cls_and_vals, strat):
    """
    Simple classes can be unstructured and restructured with forbid_extra_keys=True.
    """
    converter = Converter(unstruct_strat=strat, forbid_extra_keys=True)
    cl, vals = cls_and_vals
    inst = cl(*vals)
    unstructured = converter.unstructure(inst)
    assert "Hyp" not in repr(unstructured)
    assert inst == converter.structure(unstructured, cl)
Beispiel #17
0
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)
Beispiel #18
0
def create_cattrs_converter():
    converter = Converter()
    converter.register_structure_hook(bool, _structure_bool)
    converter.register_structure_hook(string_type, _structure_string)
    converter.register_structure_hook(Model, _structure_schematics)
    converter.register_structure_hook(BaseType, _structure_basetype)
    converter.register_structure_hook(datetime, _structure_datetime)
    converter.register_unstructure_hook(Model, _unstructure_schematics)
    converter.register_unstructure_hook(datetime, _unstructure_datetime)
    converter.register_unstructure_hook(BaseType, _unstructure_basetype)
    return converter
Beispiel #19
0
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]
Beispiel #20
0
def test_forbid_extra_keys_defaults(attr_and_vals):
    """
    Restructuring fails when a dict key is renamed (if forbid_extra_keys set)
    """
    a, _ = attr_and_vals
    cl = make_class("HypClass", {"a": a})
    converter = Converter(forbid_extra_keys=True)
    inst = cl()
    unstructured = converter.unstructure(inst)
    unstructured["aa"] = unstructured.pop("a")
    with pytest.raises(Exception):
        converter.structure(unstructured, cl)
Beispiel #21
0
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}
Beispiel #22
0
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
Beispiel #23
0
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
Beispiel #24
0
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
Beispiel #25
0
def test_forbid_extra_keys(cls_and_vals):
    """
    Restructuring fails when extra keys are present (when configured)
    """
    converter = Converter(forbid_extra_keys=True)
    cl, vals = cls_and_vals
    inst = cl(*vals)
    unstructured = converter.unstructure(inst)
    bad_key = list(unstructured)[0] + "A" if unstructured else "Hyp"
    while bad_key in unstructured:
        bad_key += "A"
    unstructured[bad_key] = 1
    with pytest.raises(Exception):
        converter.structure(unstructured, cl)
Beispiel #26
0
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
Beispiel #27
0
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
Beispiel #28
0
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
Beispiel #29
0
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)
Beispiel #30
0
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)