Skip to content

Should asyncio.iscoroutinefunction return some kind of TypeGuard? #8009

Closed
@ajoino

Description

@ajoino

I have the following use-case of a class that takes a regular function or a coroutine as an attribute, and then provides access to it through two properties. Basically the code looks like this:

"""example.py"""

from asyncio import iscoroutinefunction
from typing import TypeVar, Generic, Callable, Awaitable, Any

T = TypeVar('T')

class Response(Generic[T]):
    ...

class Request(Generic[T]):
    ...

class Foo:
    def __init__(self, callback: Callable[[Request[T]], Response[T] | Awaitable[Response[T]]]):
        self._callback = callback

    @property
    def func(self) -> Callable[[Request[T]], Response[T]]:
        if iscoroutinefunction(self._callback):
            raise AttributeError()

        return self._callback

    @property
    def coro(self) -> Callable[[Request[T]], Awaitable[Response[T]]:
        if not iscoroutinefunction(self._callback):
            raise AttributeError()

        return self._callback

async def bar(req: Request[T]) -> Response[T]:
    ...

foo = Foo(bar)

Now, I expected mypy to give no errors due to type narrowing from `iscoroutinefunction, but instead I get

example.py:21: error: Incompatible return value type (got "Callable[[Request[T]], Union[Response[T], Awaitable[Response[T]]]]", expected "Callable[[Request[T]], Response[T]]")
example.py:28: error: Incompatible return value type (got "Callable[[Request[T]], Union[Response[T], Awaitable[Response[T]]]]", expected "Callable[[Request[T]], Awaitable[Response[T]]]")

After finding this, I started searching through the issues in both mypy and typeshed but couldn't find anything related to iscoroutinefunction. I then looked into typeshed and saw that the return type of iscoroutinefunction is just bool, and not a TypeGuard, despite the similar iscoroutine having one. This makes me think that iscoroutinefunction is annotated like that on purpose.

So my question is, should the return type of asyncio.iscoroutinefunction be a TypeGuard of some kind?

I'm new to the more advanced features of Python typing, and my naive implementation would be something like

from typing import TypeVar, TypeGuard

T = TypeVar('T')

def iscoroutinefunction(func: T) -> TypeGuard[T]: ...

If you feel like this should be changed, I'd be happy to submit a PR once I know what the type should be.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions