The right way to type hint a Coroutine function?
Question:
I cannot wrap my head around type hinting a Coroutine
. As far as I understand, when we declare a function like so:
async def some_function(arg1: int, arg2: str) -> list:
...
we effectively declare a function, which returns a coroutine, which, when awaited, returns a list. So, the way to type hint it would be:
f: Callable[[int, str], Coroutine[???]] = some_function
But Coroutine
generic type has 3 arguments! We can see it if we go to the typing.py
file:
...
Coroutine = _alias(collections.abc.Coroutine, 3)
...
There is also Awaitable
type, which logically should be a parent of Coroutine
with only one generic parameter (the return type, I suppose):
...
Awaitable = _alias(collections.abc.Awaitable, 1)
...
So maybe it would be more or less correct to type hint the function this way:
f: Callable[[int, str], Awaitable[list]] = some_function
Or is it?
So, basically, the questions are:
- Can one use
Awaitable
instead of Coroutine
in the case of type hinting an async def
function?
- What are the correct parameters for the
Coroutine
generic type and what are its use-cases?
Answers:
As the docs state:
Coroutine
objects and instances of the Coroutine
ABC are all instances of the Awaitable
ABC.
And for the Coroutine
type:
A generic version of collections.abc.Coroutine
. The variance and order of type variables correspond to those of Generator
.
Generator
in turn has the signature Generator[YieldType, SendType, ReturnType]
. So if you want to preserve that type information, use Coroutine
, otherwise Awaitable
should suffice.
The Coroutine
type takes the same signature as the Generator
type:
Generator[YieldType, SendType, ReturnType]
Since the result of an unawaited call to a coroutine is an awaitable (it can be used in an await
expression), it can be type hinted with:
Awaitable[ReturnType]
Example:
async def some_function(arg1: int, arg2: str) -> List[str]:
return ['foo']
coro: Awaitable[List[str]] = some_function(1, 'bar')
result = await coro
print(result)
# prints ['foo']
However, for type hinting a coroutine, I do not find either of these useful on their own.
Instead, I opt for something similar to what you stated in your last example:
def return_coro() -> Callable[[int, str], Awaitable[List[str]]]:
async def some_function(arg1: int, arg2: str) -> List[str]:
return ['foo']
return some_function
Note mypy will get upset if you try to pass this into a function which explicitly expects a Coroutine (such as asyncio.run()).
I cannot wrap my head around type hinting a Coroutine
. As far as I understand, when we declare a function like so:
async def some_function(arg1: int, arg2: str) -> list:
...
we effectively declare a function, which returns a coroutine, which, when awaited, returns a list. So, the way to type hint it would be:
f: Callable[[int, str], Coroutine[???]] = some_function
But Coroutine
generic type has 3 arguments! We can see it if we go to the typing.py
file:
...
Coroutine = _alias(collections.abc.Coroutine, 3)
...
There is also Awaitable
type, which logically should be a parent of Coroutine
with only one generic parameter (the return type, I suppose):
...
Awaitable = _alias(collections.abc.Awaitable, 1)
...
So maybe it would be more or less correct to type hint the function this way:
f: Callable[[int, str], Awaitable[list]] = some_function
Or is it?
So, basically, the questions are:
- Can one use
Awaitable
instead ofCoroutine
in the case of type hinting anasync def
function? - What are the correct parameters for the
Coroutine
generic type and what are its use-cases?
As the docs state:
Coroutine
objects and instances of theCoroutine
ABC are all instances of theAwaitable
ABC.
And for the Coroutine
type:
A generic version of
collections.abc.Coroutine
. The variance and order of type variables correspond to those ofGenerator
.
Generator
in turn has the signature Generator[YieldType, SendType, ReturnType]
. So if you want to preserve that type information, use Coroutine
, otherwise Awaitable
should suffice.
The Coroutine
type takes the same signature as the Generator
type:
Generator[YieldType, SendType, ReturnType]
Since the result of an unawaited call to a coroutine is an awaitable (it can be used in an await
expression), it can be type hinted with:
Awaitable[ReturnType]
Example:
async def some_function(arg1: int, arg2: str) -> List[str]:
return ['foo']
coro: Awaitable[List[str]] = some_function(1, 'bar')
result = await coro
print(result)
# prints ['foo']
However, for type hinting a coroutine, I do not find either of these useful on their own.
Instead, I opt for something similar to what you stated in your last example:
def return_coro() -> Callable[[int, str], Awaitable[List[str]]]:
async def some_function(arg1: int, arg2: str) -> List[str]:
return ['foo']
return some_function
Note mypy will get upset if you try to pass this into a function which explicitly expects a Coroutine (such as asyncio.run()).