Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

prefer_attrib_converters doesn't work when the field is aliased #527

Closed
ljnsn opened this issue Mar 28, 2024 · 2 comments · Fixed by #528
Closed

prefer_attrib_converters doesn't work when the field is aliased #527

ljnsn opened this issue Mar 28, 2024 · 2 comments · Fixed by #528

Comments

@ljnsn
Copy link

ljnsn commented Mar 28, 2024

  • cattrs version: 23.2.3
  • Python version: 3.11
  • Operating System: Linux

Description

I want to use the converter set on a field of an attrs model of mine. The field also has an alias. Trying to structure with cattrs doesn't work in this case.

What I Did

import enum
from typing import Any, Callable, Mapping

import attrs
import cattrs
from cattrs.gen import make_dict_unstructure_fn, make_dict_structure_fn


class X(enum.Enum):
    A = "a"
    B = "b"
    OTHER = "other"


def _x_or_other(value: Any) -> X:
    try:
        return X(value)
    except ValueError:
        return X.OTHER


@attrs.define()
class Bar:
    x: X = attrs.field(converter=_x_or_other, alias="Y")


converter = cattrs.Converter(prefer_attrib_converters=True)


def _to_alias_unstructure(cls: type[Any]) -> Callable[[Any], dict[str, Any]]:
    """Unstructure hook using alias."""
    return make_dict_unstructure_fn(
        cls,
        converter,
        _cattrs_use_alias=True,
    )


def _to_alias_structure(
    cls: type[Any],
) -> Callable[[Mapping[str, Any], Any], Callable[[Any, Any], Any]]:
    """Structure hook using alias."""
    return make_dict_structure_fn(
        cls,
        converter,
        _cattrs_use_alias=True,
    )


converter.register_unstructure_hook_factory(attrs.has, _to_alias_unstructure)
converter.register_structure_hook_factory(attrs.has, _to_alias_structure)

bar1 = {"Y": "a"}
bar2 = {"Y": "whatever"}

converter.structure(bar1, Bar)  # works
converter.structure(bar2, Bar)  # fails
@Tinche
Copy link
Member

Tinche commented Mar 28, 2024

Hi,

if you're overriding the entire attrs hook you'll need to pass in _cattrs_prefer_attrib_converter yourself, like this:

def _to_alias_structure(
    cls: type[Any],
) -> Callable[[Mapping[str, Any], Any], Callable[[Any, Any], Any]]:
    """Structure hook using alias."""
    return make_dict_structure_fn(
        cls, converter, _cattrs_use_alias=True, _cattrs_prefer_attrib_converters=True
    )

That said, I realize it'd be better if make_dict_structure_fn took the value of that parameter from the converter itself, so I've created #528 to address this for the next version.

@ljnsn
Copy link
Author

ljnsn commented Mar 29, 2024

Ah makes sense, thanks for the quick response and explanation 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants