Skip to content
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

Must yield when declaring an AsyncIterator for a Protocol. #5385

Closed
euresti opened this issue Jul 24, 2018 · 2 comments
Closed

Must yield when declaring an AsyncIterator for a Protocol. #5385

euresti opened this issue Jul 24, 2018 · 2 comments

Comments

@euresti
Copy link
Contributor

euresti commented Jul 24, 2018

I think I've gone off the async ledge here but I was converting a Protocol I had to have async support and I started getting some weird errors.

from typing import List
from typing_extensions import Protocol, AsyncIterator

class MyProtocol(Protocol):
    async def foo(self) -> AsyncIterator[int]:
        ...

class Foo:
    async def foo(self) -> AsyncIterator[int]:
        yield 5
        yield 6

async def foo(p: Foo) -> List[int]:
    return [item async for item in p.foo()]

async def bar(p: MyProtocol) -> List[int]:
    return [item async for item in p.foo()]

x: MyProtocol = Foo()
foo.py:17: error: "Coroutine[Any, Any, AsyncIterator[int]]" has no attribute "__aiter__" (not async iterable)
foo.py:19: error: Incompatible types in assignment (expression has type "Foo", variable has type "MyProtocol")
foo.py:19: note: Following member(s) of "Foo" have conflicts:
foo.py:19: note:     Expected:
foo.py:19: note:         def foo(self) -> Coroutine[Any, Any, AsyncIterator[int]]
foo.py:19: note:     Got:
foo.py:19: note:         def foo(self) -> AsyncIterator[int]

However I can get around the error by doing the following:

class Weird(Protocol):
    async def foo(self) -> AsyncIterator[int]:
        if False:
            yield

async def weird(p: Weird) -> List[int]:
    return [item async for item in p.foo()]

y: Weird = Foo()

I'm guessing adding the yield is necessary in this case? Is there a better way to do this than if False?
mypy 0.620
Python 3.6.5

@JelleZijlstra
Copy link
Member

I think you shouldn't make the protocol function async def, but just def. Conceptually, an async generator is a callable that returns an AsyncIterator (or more precisely, an AsyncGenerator). But an async def function without a yield returns an Awaitable of whatever its declared return type is, so that's how mypy interprets your protocol.

@euresti
Copy link
Contributor Author

euresti commented Jul 24, 2018

That works. Thanks!

@euresti euresti closed this as completed Jul 24, 2018
UncleGoogle added a commit to UncleGoogle/galaxy-integration-humblebundle that referenced this issue May 16, 2022
it is due to wrong annotation in plugins API, see
python/mypy#5385 (comment)
UncleGoogle added a commit to UncleGoogle/galaxy-integration-humblebundle that referenced this issue May 16, 2022
it is due to wrong annotation in plugins API, see
python/mypy#5385 (comment)
pgjones added a commit to pgjones/quart-db that referenced this issue Oct 11, 2022
As per python/mypy#5385 the protocol
AsyncGenerator method should be a `def` not `async def`.
hauntsaninja added a commit to hauntsaninja/mypy that referenced this issue Mar 29, 2023
Some of the examples here neither ran nor type checked.
Remove mention and use of deprecated APIs.

Also go into some detail about async generators. Document python#5385
since it comes up not infrequently.

Linking python#13681
hauntsaninja added a commit that referenced this issue Mar 30, 2023
Some of the examples here neither ran nor type checked. Remove mention
and use of deprecated APIs.

Also go into some detail about async generators. Document #5385 since it
comes up not infrequently.

Linking #13681
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants