Skip to content

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

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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions