Skip to content

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

@euresti

Description

@euresti

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

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

    Issue actions