Skip to content

Mypy doesn't work well with AsyncGenerator and @abstractmethod #18681

Closed as duplicate

Description

@1st

Bug Report

Mypy doesn't work well with AsyncGenerator and @abstractmethod

To Reproduce

Working (without async def): https://mypy-play.net/?mypy=latest&python=3.12&gist=b3f4c282f2435ac7bbbee2f435de3de7

Not working (with async def): https://mypy-play.net/?mypy=latest&python=3.12&gist=83f18b68c8ce201ecc907714bf947571

Image

Expected Behavior

I want to define "async def" method with @abstractmethod decorator, to show that all subclasses need to use asynchronous version. But problem is that when I do this - mypy gets crazy and tells that it has a problem in implementation of the interface method.

I think that problem is in usage of yield in the actual method to make it a generator, and just pass for the abstract method, that is missing yield. but in this case we sacrifice the correct definition of the base method and it seems misleading for developer that want to implement the base method that is missing he async keyword.

Actual Behavior

Return type "AsyncGenerator[int, None]" of "get" incompatible with return type "Coroutine[Any, Any, AsyncGenerator[int, None]]" in supertype "Base" [override]

Activity

  1. added
    bugmypy got something wrong
    on Feb 14, 2025
  2. TeamSpen210 commented on Feb 14, 2025

    @TeamSpen210
    Contributor

    Yep, it's the lack of yield, meaning the checker just wraps AsyncGenerator in Coroutine. If you make the abstract method a synchronous method it'll work fine, since the only difference between the different function types is their return type.

  3. 1st commented on Feb 14, 2025

    @1st
    Author

    Yep, it's the lack of yield, meaning the checker just wraps AsyncGenerator in Coroutine. If you make the abstract method a synchronous method it'll work fine, since the only difference between the different function types is their return type.

    Can you give an example of how to define asynchronous def as abstract method? Maybe I miss something

  4. TeamSpen210 commented on Feb 15, 2025

    @TeamSpen210
    Contributor

    Just do def func(self) -> Coroutine[int]:, or the same with AsyncGenerator, Generator or superclasses of those. It’s more explicit, but there’s no wrapping going on so the result is clear.

  5. 1st commented on Feb 15, 2025

    @1st
    Author

    Just do def func(self) -> Coroutine[int]:, or the same with AsyncGenerator, Generator or superclasses of those. It’s more explicit, but there’s no wrapping going on so the result is clear.

    My target goal is to define asynchronous abstract method to show the interface to use to implement a final class. I posted two links with code and just wondering how to tell mypy to keep my abstract as it is. Maybe I need use another decorator, like async_abstract or something like this?

  6. sterliakov commented on Feb 15, 2025

    @sterliakov
    Collaborator

    This is just another manifestation of #5385 with s/Protocol/ABC/ (or also #5070 with more discussion, if you prefer). The most common approach that still uses async def for intuitively correct interface is

    class Base(ABC):
        @abstractmethod
        async def get(self, start: int, end: int) -> AsyncGenerator[int, None]:
            if False: yield 1
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

    bugmypy got something wrong

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions