Description
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.