Skip to content

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

@euresti

Description

@euresti
Contributor

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

Activity

JelleZijlstra

JelleZijlstra commented on Jul 24, 2018

@JelleZijlstra
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

euresti commented on Jul 24, 2018

@euresti
ContributorAuthor

That works. Thanks!

added a commit that references this issue on Oct 11, 2022
added a commit that references this issue on Mar 29, 2023
added a commit that references this issue on Mar 30, 2023

8 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @JelleZijlstra@euresti

        Issue actions

          Must yield when declaring an AsyncIterator for a Protocol. · Issue #5385 · python/mypy