Skip to content

Commit 7235f7b

Browse files
authored
More modernization (#592)
* More modernization * Improve coverage * Relock * Remove dead code * More coverage
1 parent dce7347 commit 7235f7b

File tree

14 files changed

+121
-234
lines changed

14 files changed

+121
-234
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
python-version: "${{ matrix.python-version }}"
2626
allow-python-prereleases: true
2727
cache: true
28-
version: "2.18.1"
28+
version: "2.19.2"
2929

3030
- name: "Run Tox"
3131
run: |
@@ -107,6 +107,7 @@ jobs:
107107
- uses: "pdm-project/setup-pdm@v4"
108108
with:
109109
python-version: "3.12"
110+
version: "2.19.2"
110111

111112
- name: "Install check-wheel-content and twine"
112113
run: "python -m pip install twine check-wheel-contents"

pdm.lock

Lines changed: 2 additions & 41 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cattrs/_compat.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,11 @@ def fields_dict(type) -> dict[str, Union[Attribute, Field]]:
149149
return attrs_fields_dict(type)
150150

151151

152-
def adapted_fields(cl) -> list[Attribute]:
153-
"""Return the attrs format of `fields()` for attrs and dataclasses."""
152+
def adapted_fields(cl: type) -> list[Attribute]:
153+
"""Return the attrs format of `fields()` for attrs and dataclasses.
154+
155+
Resolves `attrs` stringified annotations, if present.
156+
"""
154157
if is_dataclass(cl):
155158
attrs = dataclass_fields(cl)
156159
if any(isinstance(a.type, str) for a in attrs):

src/cattrs/gen/__init__.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from collections.abc import Iterable, Mapping
55
from typing import TYPE_CHECKING, Any, Callable, Final, Literal, TypeVar
66

7-
from attrs import NOTHING, Attribute, Factory, resolve_types
7+
from attrs import NOTHING, Attribute, Factory
88
from typing_extensions import NoDefault
99

1010
from .._compat import (
@@ -240,10 +240,6 @@ def make_dict_unstructure_fn(
240240
origin = get_origin(cl)
241241
attrs = adapted_fields(origin or cl) # type: ignore
242242

243-
if any(isinstance(a.type, str) for a in attrs):
244-
# PEP 563 annotations - need to be resolved.
245-
resolve_types(cl)
246-
247243
mapping = {}
248244
if is_generic(cl):
249245
mapping = generate_mapping(cl, mapping)
@@ -743,10 +739,6 @@ def make_dict_structure_fn(
743739

744740
attrs = adapted_fields(cl)
745741

746-
if any(isinstance(a.type, str) for a in attrs):
747-
# PEP 563 annotations - need to be resolved.
748-
resolve_types(cl)
749-
750742
# We keep track of what we're generating to help with recursive
751743
# class graphs.
752744
try:

src/cattrs/gen/typeddicts.py

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from typing import TYPE_CHECKING, Any, Callable, Literal, TypeVar
66

77
from attrs import NOTHING, Attribute
8+
from typing_extensions import _TypedDictMeta
89

910
try:
1011
from inspect import get_annotations
@@ -15,17 +16,8 @@ def get_annots(cl) -> dict[str, Any]:
1516
except ImportError:
1617
# https://docs.python.org/3/howto/annotations.html#accessing-the-annotations-dict-of-an-object-in-python-3-9-and-older
1718
def get_annots(cl) -> dict[str, Any]:
18-
if isinstance(cl, type):
19-
ann = cl.__dict__.get("__annotations__", {})
20-
else:
21-
ann = getattr(cl, "__annotations__", {})
22-
return ann
23-
19+
return cl.__dict__.get("__annotations__", {})
2420

25-
try:
26-
from typing_extensions import _TypedDictMeta
27-
except ImportError:
28-
_TypedDictMeta = None
2921

3022
from .._compat import (
3123
TypedDict,
@@ -535,8 +527,6 @@ def _adapted_fields(cls: Any) -> list[Attribute]:
535527

536528

537529
def _is_extensions_typeddict(cls) -> bool:
538-
if _TypedDictMeta is None:
539-
return False
540530
return cls.__class__ is _TypedDictMeta or (
541531
is_generic(cls) and (cls.__origin__.__class__ is _TypedDictMeta)
542532
)

tests/test_gen_dict.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,18 +336,20 @@ def test_overriding_struct_hook(converter: BaseConverter) -> None:
336336
class A:
337337
a: int
338338
b: str
339+
c: int = 0
339340

340341
converter.register_structure_hook(
341342
A,
342343
make_dict_structure_fn(
343344
A,
344345
converter,
345346
a=override(struct_hook=lambda v, _: ceil(v)),
347+
c=override(struct_hook=lambda v, _: ceil(v)),
346348
_cattrs_detailed_validation=converter.detailed_validation,
347349
),
348350
)
349351

350-
assert converter.structure({"a": 0.5, "b": 1}, A) == A(1, "1")
352+
assert converter.structure({"a": 0.5, "b": 1, "c": 0.5}, A) == A(1, "1", 1)
351353

352354

353355
def test_overriding_unstruct_hook(converter: BaseConverter) -> None:

tests/test_gen_dict_563.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from dataclasses import dataclass
66

7-
from attr import define
7+
from attrs import define
88

99
from cattrs import Converter
1010
from cattrs.gen import make_dict_structure_fn, make_dict_unstructure_fn

tests/test_generics.py

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
T = TypeVar("T")
1616
T2 = TypeVar("T2")
17+
T3 = TypeVar("T3", bound=int)
1718

1819

1920
def test_deep_copy():
@@ -31,9 +32,10 @@ def test_deep_copy():
3132

3233

3334
@define
34-
class TClass(Generic[T, T2]):
35+
class TClass(Generic[T, T2, T3]):
3536
a: T
3637
b: T2
38+
c: T3 = 0
3739

3840

3941
@define
@@ -44,15 +46,15 @@ class GenericCols(Generic[T]):
4446

4547

4648
@pytest.mark.parametrize(
47-
("t", "t2", "result"),
49+
("t", "t2", "t3", "result"),
4850
(
49-
(int, str, TClass(1, "a")),
50-
(str, str, TClass("1", "a")),
51-
(List[int], str, TClass([1, 2, 3], "a")),
51+
(int, str, int, TClass(1, "a")),
52+
(str, str, int, TClass("1", "a")),
53+
(List[int], str, int, TClass([1, 2, 3], "a")),
5254
),
5355
)
54-
def test_able_to_structure_generics(converter: BaseConverter, t, t2, result):
55-
res = converter.structure(asdict(result), TClass[t, t2])
56+
def test_able_to_structure_generics(converter: BaseConverter, t, t2, t3, result):
57+
res = converter.structure(asdict(result), TClass[t, t2, t3])
5658

5759
assert res == result
5860

@@ -103,20 +105,20 @@ class GenericCols(Generic[T]):
103105

104106

105107
@pytest.mark.parametrize(
106-
("t", "t2", "result"),
108+
("t", "t2", "t3", "result"),
107109
(
108-
(TClass[int, int], str, TClass(TClass(1, 2), "a")),
109-
(List[TClass[int, int]], str, TClass([TClass(1, 2)], "a")),
110+
(TClass[int, int, int], str, int, TClass(TClass(1, 2), "a")),
111+
(List[TClass[int, int, int]], str, int, TClass([TClass(1, 2)], "a")),
110112
),
111113
)
112-
def test_structure_nested_generics(converter: BaseConverter, t, t2, result):
113-
res = converter.structure(asdict(result), TClass[t, t2])
114+
def test_structure_nested_generics(converter: BaseConverter, t, t2, t3, result):
115+
res = converter.structure(asdict(result), TClass[t, t2, t3])
114116

115117
assert res == result
116118

117119

118120
def test_able_to_structure_deeply_nested_generics_gen(converter):
119-
cl = TClass[TClass[TClass[int, int], int], int]
121+
cl = TClass[TClass[TClass[int, int, int], int, int], int, int]
120122
result = TClass(TClass(TClass(1, 2), 3), 4)
121123

122124
res = converter.structure(asdict(result), cl)
@@ -130,7 +132,7 @@ class TClass2(Generic[T]):
130132
c: T
131133

132134
data = TClass2(c="string")
133-
res = converter.structure(asdict(data), Union[TClass[int, int], TClass2[str]])
135+
res = converter.structure(asdict(data), Union[TClass[int, int, int], TClass2[str]])
134136
assert res == data
135137

136138

@@ -141,7 +143,7 @@ class TClass2(Generic[T]):
141143

142144
data = [TClass2(c="string"), TClass(1, 2)]
143145
res = converter.structure(
144-
[asdict(x) for x in data], List[Union[TClass[int, int], TClass2[str]]]
146+
[asdict(x) for x in data], List[Union[TClass[int, int, int], TClass2[str]]]
145147
)
146148
assert res == data
147149

@@ -153,7 +155,7 @@ class TClass2(Generic[T]):
153155

154156
data = deque((TClass2(c="string"), TClass(1, 2)))
155157
res = converter.structure(
156-
[asdict(x) for x in data], Deque[Union[TClass[int, int], TClass2[str]]]
158+
[asdict(x) for x in data], Deque[Union[TClass[int, int, int], TClass2[str]]]
157159
)
158160
assert res == data
159161

tests/test_preconf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from json import dumps as json_dumps
55
from json import loads as json_loads
66
from platform import python_implementation
7-
from typing import Any, Dict, Final, List, NamedTuple, NewType, Tuple, Union
7+
from typing import Any, Dict, Final, List, NamedTuple, NewType, Union
88

99
import pytest
1010
from attrs import define
@@ -190,7 +190,7 @@ def native_unions(
190190
include_datetimes=True,
191191
include_objectids=False,
192192
include_literals=True,
193-
) -> Tuple[Any, Any]:
193+
) -> tuple[Any, Any]:
194194
types = []
195195
strats = {}
196196
if include_strings:

tests/test_unions.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from typing import Type, Union
22

3-
import attr
43
import pytest
4+
from attrs import define
55

66
from cattrs.converters import BaseConverter, Converter
77

@@ -18,11 +18,11 @@ def test_custom_union_toplevel_roundtrip(cls: Type[BaseConverter]):
1818
"""
1919
c = cls()
2020

21-
@attr.define
21+
@define
2222
class A:
2323
a: int
2424

25-
@attr.define
25+
@define
2626
class B:
2727
a: int
2828

@@ -51,11 +51,11 @@ def test_310_custom_union_toplevel_roundtrip(cls: Type[BaseConverter]):
5151
"""
5252
c = cls()
5353

54-
@attr.define
54+
@define
5555
class A:
5656
a: int
5757

58-
@attr.define
58+
@define
5959
class B:
6060
a: int
6161

@@ -83,15 +83,15 @@ def test_custom_union_clsfield_roundtrip(cls: Type[BaseConverter]):
8383
"""
8484
c = cls()
8585

86-
@attr.define
86+
@define
8787
class A:
8888
a: int
8989

90-
@attr.define
90+
@define
9191
class B:
9292
a: int
9393

94-
@attr.define
94+
@define
9595
class C:
9696
f: Union[A, B]
9797

0 commit comments

Comments
 (0)