Skip to content

asyncio.create_task doesn't accept async_generator_asend objects #10185

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
kumaraditya303 opened this issue May 15, 2023 · 6 comments
Closed
Labels
stubs: false positive Type checkers report false errors topic: asyncio Asyncio-related issues

Comments

@kumaraditya303
Copy link
Contributor

kumaraditya303 commented May 15, 2023

The following code fails to typecheck with mypy but works just fine at runtime.

import asyncio
from typing import AsyncGenerator


async def func() -> AsyncGenerator[int, None]:
    yield 1


async def main() -> None:
    a = func()
    print(await asyncio.create_task(a.asend(None)))

asyncio.run(main())

Error:

mypy main.py
main.py:11: error: Argument 1 to "create_task" has incompatible type "Awaitable[int]"; expected "Union[Generator[Any, None, <nothing>], Coroutine[Any, Any, <nothing>]]"  [arg-type]
main.py:11: error: Argument 1 to "create_task" has incompatible type "Awaitable[int]"; expected "Union[Generator[Any, None, object], Coroutine[Any, Any, object]]"  [arg-type]
Found 2 errors in 1 file (checked 1 source file)

mypy version: mypy 1.0.0 (compiled: yes)

@kumaraditya303
Copy link
Contributor Author

cc @AlexWaygood

@srittau
Copy link
Collaborator

srittau commented May 15, 2023

@kumaraditya303 Could you give the error message, and the line the error appears in? Also, which mypy version are you using?

@kumaraditya303
Copy link
Contributor Author

@srittau Updated comment to include details.

@AlexWaygood
Copy link
Member

AlexWaygood commented May 15, 2023

Here's a mypy playground link showing that it also reproduces on mypy 1.3.0. The cause of the error is that our stubs for asyncio.create_task say that it expects a generator or a coroutine, and our stubs for typing.AsyncGenerator.asend say that that method returns Awaitable[T_co] (not Coroutine[Any, Any, int] or Generator[Any, Any, int]).

Our annotations for asyncio.create_task look correct to me:

if sys.version_info >= (3, 11):
def create_task(coro: _CoroutineLike[_T], *, name: str | None = None, context: Context | None = None) -> Task[_T]: ...
elif sys.version_info >= (3, 8):
def create_task(coro: _CoroutineLike[_T], *, name: str | None = None) -> Task[_T]: ...
else:
def create_task(coro: _CoroutineLike[_T]) -> Task[_T]: ...

This leaves two possible fixes on the table:

  1. Maybe typing.AsyncGenerator.asend() should be a coroutine function (or should be typed as returning a coroutine) in the stub, not a synchronous function that returns an awaitable. Here's what it is now:

    typeshed/stdlib/typing.pyi

    Lines 429 to 430 in 2d5dafa

    @abstractmethod
    def asend(self, __value: _T_contra) -> Awaitable[_T_co]: ...

    Note, however, that we previously made AsyncGenerator.asend a coroutine function in Use async def instead of def ... -> Awaitable in typing #7105. We then reverted that in Improve various signatures that shouldn't be async def, but currently are #7491. So there's some complicated history there.

  2. Maybe mypy should infer the return type of async-generator functions to be instances of types.AsyncGeneratorType, rather than typing.AsyncGenerator, since types.AsyncGeneratorType is typed as returning a Coroutine in our stubs currently:

    def asend(self, __val: _T_contra) -> Coroutine[Any, Any, _T_co]: ...

    Currently, if you try giving an async generator function types.AsyncGeneratorType as the return type, mypy gives you this response:

    from types import AsyncGeneratorType
    
    async def func() -> AsyncGeneratorType[int, None]:  # error: The return type of an async generator function should be "AsyncGenerator" or one of its supertypes  [misc]
        yield 1

    That's not a change we'd be able to make here, though; mypy would have to make that change.

@AlexWaygood
Copy link
Member

AlexWaygood commented May 15, 2023

I'm curious if @graingert has any thoughts on the best way to resolve this, as he was involved in the discussion around #7491 and helped review that PR!

@AlexWaygood AlexWaygood added topic: asyncio Asyncio-related issues stubs: false positive Type checkers report false errors labels May 15, 2023
@graingert
Copy link
Contributor

AsyncGenerator defines an interface based on returning awaitable objects and not coroutines so it makes sense to me that people should annotate their async def generators used with create_task with AsyncGeneratorType

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stubs: false positive Type checkers report false errors topic: asyncio Asyncio-related issues
Projects
None yet
Development

No branches or pull requests

4 participants