-
-
Notifications
You must be signed in to change notification settings - Fork 67
Expand file tree
/
Copy pathtest_deferred_annotations.py
More file actions
98 lines (71 loc) · 2.45 KB
/
Copy pathtest_deferred_annotations.py
File metadata and controls
98 lines (71 loc) · 2.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
"""Tests for PEP 649 deferred annotations (Python 3.14+).
On Python 3.14 annotations are evaluated lazily via ``__annotate__``.
Copying ``fn.__annotations__`` eagerly at decoration time forces that
evaluation and crashes with ``NameError`` when the annotation refers to
a name defined after the decorated function, a pattern that works with
``functools.lru_cache``.
"""
import inspect
import sys
from functools import lru_cache, partial
import pytest
from async_lru import alru_cache
requires_py314 = pytest.mark.skipif(
sys.version_info < (3, 14),
reason="deferred annotation evaluation requires Python 3.14",
)
@requires_py314
async def test_deco_with_annotation_defined_after() -> None:
@alru_cache(maxsize=1)
async def get_foo() -> Foo:
return Foo()
class Foo:
pass
first = await get_foo()
assert isinstance(first, Foo)
assert await get_foo() is first
@requires_py314
async def test_annotations_stay_lazy_like_lru_cache() -> None:
@alru_cache(maxsize=1)
async def get_foo_async() -> Foo:
return Foo()
@lru_cache(maxsize=1)
def get_foo_sync() -> Foo:
return Foo()
class Foo:
pass
assert isinstance(await get_foo_async(), Foo)
assert isinstance(get_foo_sync(), Foo)
assert (
inspect.get_annotations(get_foo_async)
== inspect.get_annotations(get_foo_sync)
== {"return": Foo}
)
@requires_py314
async def test_unresolvable_annotation_raises_only_on_access() -> None:
@alru_cache(maxsize=1)
async def broken() -> Missing: # type: ignore[name-defined] # noqa: F821
pass # pragma: no cover
with pytest.raises(NameError):
inspect.get_annotations(broken)
async def test_method_wrapping_partial_without_annotation_attributes() -> None:
"""A wrapped ``partial`` carries neither ``__annotate__`` nor
``__annotations__``; binding it as a method copies neither."""
async def impl(self: object) -> int:
return 42
class Api:
meth = alru_cache(partial(impl))
api = Api()
assert await api.meth() == 42
assert not hasattr(api.meth, "__annotations__")
@requires_py314
async def test_method_annotations_stay_lazy() -> None:
class Api:
@alru_cache(maxsize=1)
async def get_foo(self) -> Foo:
return Foo()
class Foo:
pass
api = Api()
assert isinstance(await api.get_foo(), Foo)
assert inspect.get_annotations(api.get_foo) == {"return": Foo}