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

AsyncIterator output type for abstract method #1473

Closed
giokara-work opened this issue Jul 19, 2023 · 3 comments
Closed

AsyncIterator output type for abstract method #1473

giokara-work opened this issue Jul 19, 2023 · 3 comments

Comments

@giokara-work
Copy link

(venv) ➜  pytype --version
2023.07.12
(venv) ➜  python --version
Python 3.10.12

Consider following example:

import abc
import asyncio
from typing import AsyncIterator

class BaseTestClass(abc.ABC):

    @abc.abstractmethod
    async def count(self) -> AsyncIterator[int]:
        raise NotImplementedError()

    async def follow(self) -> None:
        async for message in self.count():
            print(message)

class TestClass(BaseTestClass):
    async def count(self) -> AsyncIterator[int]:
        for i in range(5):
            yield i

if __name__ == '__main__':
    asyncio.run(TestClass().follow())

Output of pytype is:

(venv) ➜  pytype minimal_async_iterator.py
Computing dependencies
Analyzing 1 sources with 0 local dependencies
ninja: Entering directory `.pytype'
[1/1] check minimal_async_iterator
FAILED: <dir>/.pytype/pyi/minimal_async_iterator.pyi 
<dir>/venv/bin/python3.10 -m pytype.single --imports_info <dir>/.pytype/imports/minimal_async_iterator.imports --module-name minimal_async_iterator --platform darwin -V 3.10 -o <dir>/.pytype/pyi/minimal_async_iterator.pyi --analyze-annotated --nofail --quick <dir>/minimal_async_iterator.py
File "<dir>/minimal_async_iterator.py", line 17, in follow: No attribute '__aiter__' on Coroutine[Any, Any, AsyncIterator[int]] [attribute-error]
File "<dir>/minimal_async_iterator.py", line 21, in TestClass: Overriding method signature mismatch [signature-mismatch]
  Base signature: 'def BaseTestClass.count(self) -> Coroutine[Any, Any, AsyncIterator[int]]'.
  Subclass signature: 'def TestClass.count(self) -> AsyncIterator[int]'.
  Return type mismatch.

For more details, see https://google.github.io/pytype/errors.html
ninja: build stopped: subcommand failed.
Leaving directory '.pytype'

Simply removing the async keyword from the definition of BaseTestClass.count makes pytype run successfully. This is unexpected, since now the derived function TestClass.count does not have the same signature as its base class. Running pylint on the file will now instead fail.

@giokara-work giokara-work changed the title AsyncIterator in abstract method AsyncIterator output from abstract method Jul 19, 2023
@giokara-work giokara-work changed the title AsyncIterator output from abstract method AsyncIterator output type for abstract method Jul 19, 2023
@eltoder
Copy link

eltoder commented Jul 19, 2023

This is actually correct and works exactly the same way in mypy:

async_iter.py:12: error: "Coroutine[Any, Any, AsyncIterator[int]]" has no attribute "__aiter__" (not async iterable)  [attr-defined]
async_iter.py:12: note: Maybe you forgot to use "await"?
async_iter.py:16: error: Return type "AsyncIterator[int]" of "count" incompatible with return type "Coroutine[Any, Any, AsyncIterator[int]]" in supertype "BaseTestClass"  [override]

See here for the explanation: https://stackoverflow.com/a/68911014
Or these mypy issues for more of the same: python/mypy#5385 python/mypy#5070

If removing async makes pylint fail, this is probably a bug in pylint.

@eltoder
Copy link

eltoder commented Jul 19, 2023

This is also now explained in mypy docs. See the last example on this page: https://mypy.readthedocs.io/en/latest/more_types.html#typing-async-await

@giokara-work
Copy link
Author

One common confusion is that the presence of a yield statement in an async def function has an effect on the type of the function

That was indeed my confusion. Many thanks for the references!

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