Skip to content

Commit

Permalink
GenConverter: fix issue with unstructuring attrs classes with generic…
Browse files Browse the repository at this point in the history
… fields
  • Loading branch information
Tinche committed Mar 21, 2021
1 parent 2d345ef commit 4f05fad
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 2 deletions.
2 changes: 2 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ History

1.5.0 (UNRELEASED)
------------------
* Fix an issue with ``GenConverter`` unstructuring ``attrs`` classes and dataclasses with generic fields.
(`#65 </~https://github.com/Tinche/cattrs/issues/65>`_)

1.4.0 (2021-03-21)
------------------
Expand Down
5 changes: 5 additions & 0 deletions src/cattr/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ def has(cls):
)


def has_with_generic(cls):
"""Test whether the class if a normal or generic attrs or dataclass."""
return has(cls) or has(get_origin(cls))


def fields(type):
try:
return type.__attrs_attrs__
Expand Down
6 changes: 5 additions & 1 deletion src/cattr/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
is_annotated,
has,
fields,
has_with_generic,
)
from .disambiguators import create_uniq_field_dis_func
from .dispatch import MultiStrategyDispatch
Expand Down Expand Up @@ -490,7 +491,7 @@ def __init__(
self._unstructure_func.register_func_list(
[
(
has,
has_with_generic,
self.gen_unstructure_attrs_fromdict,
True,
),
Expand Down Expand Up @@ -541,6 +542,9 @@ def gen_structure_annotated(self, type):
return h

def gen_unstructure_attrs_fromdict(self, cl: Type[T]) -> Dict[str, Any]:
origin = get_origin(cl)
if origin is not None:
cl = origin
attribs = fields(cl)
if any(isinstance(a.type, str) for a in attribs):
# PEP 563 annotations - need to be resolved.
Expand Down
28 changes: 27 additions & 1 deletion tests/test_generics.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import pytest
from attr import asdict, attrs

from cattr import Converter
from cattr import Converter, GenConverter

T = TypeVar("T")
T2 = TypeVar("T2")
Expand Down Expand Up @@ -83,3 +83,29 @@ def test_raises_if_no_generic_params_supplied(converter):
match="Unsupported type: ~T. Register a structure hook for it.",
):
converter.structure(asdict(data), TClass)


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"))

0 comments on commit 4f05fad

Please sign in to comment.