← Back to index

annotations_generators.py

True Positive
False Positive
False Negative
Optional (detected)
Warning or Info
TP: 2
FP: 0
FN: 8
Optional: 0 / 3
1"""
2Tests for annotating generators.
3"""
4
5# Specification: https://typing.readthedocs.io/en/latest/spec/annotations.html#annotating-generator-functions-and-coroutines
6
7# The return type of generator functions can be annotated by the generic type
8# Generator[yield_type, send_type, return_type] provided by typing.py module.
9
10import asyncio
11from typing import (
12 Any,
13 AsyncGenerator,
14 AsyncIterable,
15 AsyncIterator,
16 Awaitable,
17 Callable,
18 Coroutine,
19 Generator,
20 Iterable,
21 Iterator,
22 Protocol,
23 TypeVar,
24 assert_type,
25)
27T = TypeVar("T")
30class A:
31 pass
34class B:
35 def should_continue(self) -> bool:
36 return True
39class C:
40 pass
43def generator1() -> Generator[A, B, C]:
44 cont = B()
45 while cont.should_continue():
46 yield A()
48 return C()
51def generator2() -> Generator[A, B, C]: # E: missing return
Expected a ty diagnostic for this line
52 cont = B()
53 if cont.should_continue():
54 return False # E: incompatible return type
Expected a ty diagnostic for this line
56 while cont.should_continue():
57 yield 3 # E: incompatible yield type
Expected a ty diagnostic for this line
60def generator3() -> Generator[A, int, Any]:
61 cont = B()
62 if cont.should_continue():
63 return 3
65 while cont.should_continue():
66 yield 3 # E: Incompatible yield type
Expected a ty diagnostic for this line
69def generator4() -> Iterable[A]:
70 yield A()
71 return True # E?: No return value expected
74def generator5() -> Iterator[A]:
75 yield B() # E: incompatible yield type
Expected a ty diagnostic for this line
78def generator6() -> Generator[None, None, None]:
79 yield
82def generator7() -> Iterator[dict[str, int]]:
83 yield {"": 0} # OK
86def generator8() -> int: # E: incompatible return type
[invalid-return-type] Return type does not match returned value: expected `int`, found `types.GeneratorType`
87 yield None # E?
88 return 0
91async def generator9() -> int: # E: incompatible return type
[invalid-return-type] Return type does not match returned value: expected `int`, found `types.AsyncGeneratorType`
92 yield None # E?
95class IntIterator(Protocol):
96 def __next__(self, /) -> int:
97 ...
100def generator15() -> IntIterator: # OK
101 yield 0
104class AsyncIntIterator(Protocol):
105 def __anext__(self, /) -> Awaitable[int]:
106 ...
109async def generator16() -> AsyncIntIterator: # OK
110 yield 0
113def generator17() -> Iterator[A]: # OK
114 yield from generator17()
117def generator18() -> Iterator[B]:
118 yield from generator17() # E: incompatible generator type
Expected a ty diagnostic for this line
119 yield from [1] # E: incompatible generator type
Expected a ty diagnostic for this line
122def generator19() -> Generator[None, float, None]: # OK
123 x: float = yield
126def generator20() -> Generator[None, int, None]: # OK
127 yield from generator19()
130def generator21() -> Generator[None, int, None]:
131 x: float = yield
134def generator22() -> Generator[None, str, None]:
135 yield from generator21() # E: incompatible send type
Expected a ty diagnostic for this line
138def generator23() -> Iterable[str]: # OK
139 return
140 yield "" # Unreachable
143async def generator24() -> AsyncIterable[str]: # OK
144 return
145 yield "" # Unreachable
148def generator25(ints1: list[int], ints2: list[int]) -> Generator[int, None, None]: # OK
149 yield from ints1
150 yield from ints2
153async def get_data() -> list[int]:
154 await asyncio.sleep(1)
155 return [1, 2, 3]
158async def generator26(nums: list[int]) -> AsyncGenerator[str, None]:
159 for n in nums:
160 await asyncio.sleep(1)
161 yield f"The number is {n}"
164async def generator27() -> AsyncGenerator[str, None]:
165 data = await get_data()
166 v1 = generator26(data)
167 assert_type(v1, AsyncGenerator[str, None])
168 return v1
171async def generator28() -> AsyncIterator[str]:
172 data = await get_data()
173 v1 = generator26(data)
174 assert_type(v1, AsyncGenerator[str, None])
175 return v1
178async def generator29() -> AsyncIterator[int]:
179 raise NotImplementedError
182# Don't use assert_type here because some type checkers infer
183# the narrower type types.CoroutineType rather than typing.Coroutine
184# in this case.
185v1: Callable[[], Coroutine[Any, Any, AsyncIterator[int]]] = generator29
188async def generator30() -> AsyncIterator[int]:
189 raise NotImplementedError
190 yield
193assert_type(generator30, Callable[[], AsyncIterator[int]])