Although the following program successfully runs (and prints 42), neither pyright nor mypy accept it:
import asyncio
from collections.abc import AsyncGenerator
async def gen() -> AsyncGenerator[int, None]:
yield 42
async def main() -> None:
print(await asyncio.create_task(anext(gen())))
asyncio.run(main())
Pyright produces the following type error (mypy’s error is similar):
Argument of type "Awaitable[int]" cannot be assigned to parameter "coro" of type "_CoroutineLike[_T@create_task]" in function "create_task"
"Awaitable[int]" is not assignable to "Coroutine[Any, Any, _T@create_task]"
The reason for this type error is that the definition of the collections.abc.AsyncGenerator
protocol on typeshed says that __anext__
returns an awaitable whereas asyncio.create_task
expects an actual coroutine, both in its type and at runtime. The program above runs successfully nevertheless because the __anext__
method of an asynchronous generator defined using the async def
/yield
syntax does indeed return a coroutine. Thus, both pyright and mypy reject a very legitimate program.
There’s a second protocol for asynchronous generators, namely types.AcyncGeneratorType
. Its docs say
types.AsyncGeneratorType
The type of asynchronous generator-iterator objects, created by asynchronous generator functions.
and its definition on typeshed says that __anext__
returns a coroutine. Thus, the program above would type check if the return type of gen
was changed to types.AsyncGeneratorType[int, None]
. However, pyright and mypy insist that the return type of gen
must be collections.abc.AsyncGenerator
(or one of its super types).
I would like to solve this problem and arrive at a situation where the program above can be typed in a way that is accepted by the common type checkers for Python. I can think of two approaches:
- Change the definition of
collections.abc.AsyncGenerator
such that__anext__
returns a coroutine rather than just any awaitable. - Change the type checkers to allow for annotating
gen
’s return type astypes.AsyncGeneratorType
and adjust their type inference accordingly.
Which of these two approaches is the right one to pursue? Is there even a clear cut winner or is this something that needs detailed discussion?