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

Automatic conversion from ReturnType to Coroutine[Any, Any, ReturnType] for functions with "pass" #8580

Closed
Tayum opened this issue Mar 24, 2020 · 1 comment

Comments

@Tayum
Copy link

Tayum commented Mar 24, 2020

I guess there is a bug when checking async def functions with "empty" body, which are typed to return AsyncIterable.

Please see the snippet:

import asyncio
from abc import ABC, abstractmethod

from typing import AsyncIterable, List


class NumGetter(ABC):
    @abstractmethod
    async def get_nums(self) -> AsyncIterable[int]:
        pass

    async def get_nums_by_batches(self, batch_size: int) -> AsyncIterable[List[int]]:
        batch = []

        async for num in self.get_nums():
            batch.append(num)

            if len(batch) == batch_size:
                yield batch
                batch = []

        if batch:
            yield batch


class FiveNumGetter(NumGetter):
    async def get_nums(self) -> AsyncIterable[int]:
        for i in range(5):
            yield i
            await asyncio.sleep(0.01)


async def main() -> None:
    num_getter = FiveNumGetter()

    async for batch in num_getter.get_nums_by_batches(batch_size=2):
        print(batch)


if __name__ == '__main__':
    asyncio.run(main())

mypy produces errors, but it shouldn't because function decorated with @abstractmethod commonly has only pass, ..., raise NotImplementedError statements or docstrings in it.

Actual behavior

mypy gives errors:

scratch_1.py:15: error: "Coroutine[Any, Any, AsyncIterable[int]]" has no attribute "__aiter__" (not async iterable)
scratch_1.py:27: error: Return type "AsyncIterable[int]" of "get_nums" incompatible with return type "Coroutine[Any, Any, AsyncIterable[int]]" in supertype "NumGetter"

Expected behavior

No errors.

Versions

$ python --version
Python 3.7.5

$ mypy --version
mypy 0.761

Mypy configuration

python_version = 3.7
ignore_missing_imports = True

The easiest way I got myself escaped was to change:

    @abstractmethod
    async def get_nums(self) -> AsyncIterable[int]:
        pass

to

    @abstractmethod
    async def get_nums(self) -> AsyncIterable[int]:
        yield

But that gives mypy error:

scratch_1.py:10: error: Yield value expected

Surely I can write:

    @abstractmethod
    async def get_nums(self) -> AsyncIterable[int]:
        yield 0

and that will produce no errors, but in real application I do not have an int return type there, but rather a complex class which requires some mandatory fields to be instantiated in __init__, and it would look quite weird.

That's why to overcome this I wrote:

    @abstractmethod
    async def get_nums(self) -> AsyncIterable[int]:
        yield  # type: ignore

But that still does not feel as it is the way it should work.

@Tayum Tayum changed the title Automatic conversion from ReturnType to Coroutine[Any, Any, ReturnType] for functions decorated with @abc.abstractmethod Automatic conversion from ReturnType to Coroutine[Any, Any, ReturnType] for functions with "pass" Mar 25, 2020
@ilevkivskyi
Copy link
Member

This is a duplicate of #5070

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