Example #1
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
Example #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
Example #3
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}
Example #4
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
Example #5
0
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
Example #6
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(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
Example #7
0
    def attrs_hook_factory(cls: Type[Any],
                           gen_fn: Callable[..., Callable[[Any], Any]],
                           structuring: bool) -> Callable[[Any], Any]:
        base = get_origin(cls)
        if base is None:
            base = cls
        attribs = fields(base)
        # PEP563 postponed annotations need resolving as we check Attribute.type below
        resolve_types(base)
        kwargs: dict[str, bool | AttributeOverride] = {}
        if structuring:
            kwargs["_cattrs_forbid_extra_keys"] = conv.forbid_extra_keys
            kwargs[
                "_cattrs_prefer_attrib_converters"] = conv._prefer_attrib_converters
        else:
            kwargs["_cattrs_omit_if_default"] = conv.omit_if_default
        for a in attribs:
            if a.type in conv.type_overrides:
                # cattrs' gen_(un)structure_attrs_fromdict (used by default for attrs
                # classes that don't have a custom hook registered) check for any
                # type_overrides (Dict[Type, AttributeOverride]); they allow a custom
                # converter to omit specific attributes of given type e.g.:
                # >>> conv = GenConverter(type_overrides={Image: override(omit=True)})
                attrib_override = conv.type_overrides[a.type]
            else:
                # by default, we omit all Optional attributes (i.e. with None default),
                # overriding a Converter's global 'omit_if_default' option. Specific
                # attibutes can still define their own 'omit_if_default' behavior in
                # the Attribute.metadata dict.
                attrib_override = override(
                    omit_if_default=a.metadata.get("omit_if_default",
                                                   a.default is None or None),
                    rename=a.metadata.get(
                        "rename_attr",
                        a.name[1:] if a.name[0] == "_" else None),
                    omit=not a.init,
                )
            kwargs[a.name] = attrib_override

        return gen_fn(cls, conv, **kwargs)
# This requires that the names of the attributes are renamed during (un-)structuring.
# Additionally we only want to unstructure attributes which don't have the default value
# (e.g. Layer.default_view_configuration has many attributes which are all optionally).
for cls in [
        DatasetProperties,
        MagViewProperties,
        DatasetViewConfiguration,
        LayerViewConfiguration,
]:
    dataset_converter.register_unstructure_hook(
        cls,
        make_dict_unstructure_fn(
            cls,
            dataset_converter,
            **{
                a.name: override(omit_if_default=True,
                                 rename=snake_to_camel_case(a.name))
                for a in attr.fields(cls)  # pylint: disable=not-an-iterable
            },
        ),
    )
    dataset_converter.register_structure_hook(
        cls,
        make_dict_structure_fn(
            cls,
            dataset_converter,
            **{
                a.name: override(rename=snake_to_camel_case(a.name))
                for a in attr.fields(cls)  # pylint: disable=not-an-iterable
            },
        ),
    )
Example #9
0
 def structure_adapt_to_camel_case(type):
     overrides = {
         a.name: override(rename=to_camel_case(a.name))
         for a in fields(type)
     }
     return make_dict_structure_fn(type, converter, **overrides)