Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ $ git clone git@github.com:your_name_here/cattrs.git

```shell
$ cd cattrs/
$ poetry install
$ poetry install --all-extras
```

4. Create a branch for local development::
Expand Down
1 change: 1 addition & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
([#350](https://github.com/python-attrs/cattrs/issues/350) [#353](https://github.com/python-attrs/cattrs/pull/353))
- Subclasses structuring and unstructuring is now supported via a custom `include_subclasses` strategy.
([#312](https://github.com/python-attrs/cattrs/pull/312))
- Adds support for `typing_extensions.Annotated` when the python version is less than `3.9`. ([#366](https://github.com/python-attrs/cattrs/pull/366))

## 22.2.0 (2022-10-03)

Expand Down
5 changes: 3 additions & 2 deletions src/cattrs/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,10 @@ def get_final_base(type) -> Optional[type]:

from collections import Counter as ColCounter
from typing import Counter, Union, _GenericAlias
from typing_extensions import Annotated, get_origin

def is_annotated(_):
return False
def is_annotated(type) -> bool:
return get_origin(type) == Annotated
Comment thread
jdoiro3 marked this conversation as resolved.
Outdated

def is_tuple(type):
return type in (Tuple, tuple) or (
Expand Down
35 changes: 35 additions & 0 deletions tests/test_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -596,3 +596,38 @@ class Outer:

structured = converter.structure(raw, Outer)
assert structured == Outer(Inner(2), [Inner(2)])


def test_annotated_with_typing_extensions_attrs():
"""Annotation support works for attrs classes."""
from typing_extensions import Annotated
from typing import List

converter = Converter()

@attr.define
class Inner:
a: int

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

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