@@ -824,11 +824,11 @@ classes are generic, self-type allows giving them precise signatures:
824
824
Typing async/await
825
825
******************
826
826
827
- Mypy supports the ability to type coroutines that use the ``async/await ``
828
- syntax introduced in Python 3.5. For more information regarding coroutines and
829
- this new syntax, see :pep: ` 492 ` .
827
+ Mypy lets you type coroutines that use the ``async/await `` syntax.
828
+ For more information regarding coroutines, see :pep: ` 492 ` and the
829
+ ` asyncio documentation < https://docs.python.org/3/library/asyncio.html >`_ .
830
830
831
- Functions defined using ``async def `` are typed just like normal functions.
831
+ Functions defined using ``async def `` are typed similar to normal functions.
832
832
The return type annotation should be the same as the type of the value you
833
833
expect to get back when ``await ``-ing the coroutine.
834
834
@@ -839,65 +839,40 @@ expect to get back when ``await``-ing the coroutine.
839
839
async def format_string (tag : str , count : int ) -> str :
840
840
return f ' T-minus { count} ( { tag} ) '
841
841
842
- async def countdown_1 (tag : str , count : int ) -> str :
842
+ async def countdown (tag : str , count : int ) -> str :
843
843
while count > 0 :
844
- my_str = await format_string(tag, count) # has type ' str'
844
+ my_str = await format_string(tag, count) # type is inferred to be str
845
845
print (my_str)
846
846
await asyncio.sleep(0.1 )
847
847
count -= 1
848
848
return " Blastoff!"
849
849
850
- loop = asyncio.get_event_loop()
851
- loop.run_until_complete(countdown_1(" Millennium Falcon" , 5 ))
852
- loop.close()
850
+ asyncio.run(countdown(" Millennium Falcon" , 5 ))
853
851
854
- The result of calling an ``async def `` function *without awaiting * will be a
855
- value of type :py:class: `Coroutine[Any, Any, T] <typing.Coroutine> `, which is a subtype of
852
+ The result of calling an ``async def `` function *without awaiting * will
853
+ automatically be inferred to be a value of type
854
+ :py:class: `Coroutine[Any, Any, T] <typing.Coroutine> `, which is a subtype of
856
855
:py:class: `Awaitable[T] <typing.Awaitable> `:
857
856
858
857
.. code-block :: python
859
858
860
- my_coroutine = countdown_1 (" Millennium Falcon" , 5 )
861
- reveal_type(my_coroutine) # has type ' Coroutine[Any, Any, str]'
859
+ my_coroutine = countdown (" Millennium Falcon" , 5 )
860
+ reveal_type(my_coroutine) # Revealed type is "typing. Coroutine[Any, Any, builtins. str]"
862
861
863
- .. note ::
864
-
865
- :ref: `reveal_type() <reveal-type >` displays the inferred static type of
866
- an expression.
867
-
868
- You may also choose to create a subclass of :py:class: `~typing.Awaitable ` instead:
869
-
870
- .. code-block :: python
871
-
872
- from typing import Any, Awaitable, Generator
873
- import asyncio
862
+ .. _async-iterators :
874
863
875
- class MyAwaitable (Awaitable[str ]):
876
- def __init__ (self , tag : str , count : int ) -> None :
877
- self .tag = tag
878
- self .count = count
864
+ Asynchronous iterators
865
+ ----------------------
879
866
880
- def __await__ (self ) -> Generator[Any, None , str ]:
881
- for i in range (n, 0 , - 1 ):
882
- print (f ' T-minus { i} ( { tag} ) ' )
883
- yield from asyncio.sleep(0.1 )
884
- return " Blastoff!"
885
-
886
- def countdown_3 (tag : str , count : int ) -> Awaitable[str ]:
887
- return MyAwaitable(tag, count)
888
-
889
- loop = asyncio.get_event_loop()
890
- loop.run_until_complete(countdown_3(" Heart of Gold" , 5 ))
891
- loop.close()
892
-
893
- To create an iterable coroutine, subclass :py:class: `~typing.AsyncIterator `:
867
+ If you have an asynchronous iterator, you can use the
868
+ :py:class: `~typing.AsyncIterator ` type in your annotations:
894
869
895
870
.. code-block :: python
896
871
897
872
from typing import Optional, AsyncIterator
898
873
import asyncio
899
874
900
- class arange (AsyncIterator[ int ]) :
875
+ class arange :
901
876
def __init__ (self , start : int , stop : int , step : int ) -> None :
902
877
self .start = start
903
878
self .stop = stop
@@ -914,35 +889,78 @@ To create an iterable coroutine, subclass :py:class:`~typing.AsyncIterator`:
914
889
else :
915
890
return self .count
916
891
917
- async def countdown_4 (tag : str , n : int ) -> str :
918
- async for i in arange(n, 0 , - 1 ) :
892
+ async def run_countdown (tag : str , countdown : AsyncIterator[ int ] ) -> str :
893
+ async for i in countdown :
919
894
print (f ' T-minus { i} ( { tag} ) ' )
920
895
await asyncio.sleep(0.1 )
921
896
return " Blastoff!"
922
897
923
- loop = asyncio.get_event_loop()
924
- loop.run_until_complete(countdown_4(" Serenity" , 5 ))
925
- loop.close()
898
+ asyncio.run(run_countdown(" Serenity" , arange(5 , 0 , - 1 )))
926
899
927
- If you use coroutines in legacy code that was originally written for
928
- Python 3.4, which did not support the ``async def `` syntax, you would
929
- instead use the :py:func: `@asyncio.coroutine <asyncio.coroutine> `
930
- decorator to convert a generator into a coroutine, and use a
931
- generator type as the return type:
900
+ Async generators (introduced in :pep: `525 `) are an easy way to create
901
+ async iterators:
932
902
933
903
.. code-block :: python
934
904
935
- from typing import Any, Generator
905
+ from typing import AsyncGenerator, Optional
936
906
import asyncio
937
907
938
- @asyncio.coroutine
939
- def countdown_2 (tag : str , count : int ) -> Generator[Any, None , str ]:
940
- while count > 0 :
941
- print (f ' T-minus { count} ( { tag} ) ' )
942
- yield from asyncio.sleep(0.1 )
943
- count -= 1
944
- return " Blastoff!"
908
+ # Could also type this as returning AsyncIterator[int]
909
+ async def arange (start : int , stop : int , step : int ) -> AsyncGenerator[int , None ]:
910
+ current = start
911
+ while (step > 0 and current < stop) or (step < 0 and current > stop):
912
+ yield current
913
+ current += step
914
+
915
+ asyncio.run(run_countdown(" Battlestar Galactica" , arange(5 , 0 , - 1 )))
916
+
917
+ One common confusion is that the presence of a ``yield `` statement in an
918
+ ``async def `` function has an effect on the type of the function:
919
+
920
+ .. code-block :: python
921
+
922
+ from typing import AsyncIterator
923
+
924
+ async def arange (stop : int ) -> AsyncIterator[int ]:
925
+ # When called, arange gives you an async iterator
926
+ # Equivalent to Callable[[int], AsyncIterator[int]]
927
+ i = 0
928
+ while i < stop:
929
+ yield i
930
+ i += 1
931
+
932
+ async def coroutine (stop : int ) -> AsyncIterator[int ]:
933
+ # When called, coroutine gives you something you can await to get an async iterator
934
+ # Equivalent to Callable[[int], Coroutine[Any, Any, AsyncIterator[int]]]
935
+ return arange(stop)
936
+
937
+ async def main () -> None :
938
+ reveal_type(arange(5 )) # Revealed type is "typing.AsyncIterator[builtins.int]"
939
+ reveal_type(coroutine(5 )) # Revealed type is "typing.Coroutine[Any, Any, typing.AsyncIterator[builtins.int]]"
940
+
941
+ await arange(5 ) # Error: Incompatible types in "await" (actual type "AsyncIterator[int]", expected type "Awaitable[Any]")
942
+ reveal_type(await coroutine(5 )) # Revealed type is "typing.AsyncIterator[builtins.int]"
943
+
944
+ This can sometimes come up when trying to define base classes or Protocols:
945
+
946
+ .. code-block :: python
947
+
948
+ from typing import AsyncIterator, Protocol
949
+
950
+ class LauncherIncorrect (Protocol ):
951
+ # Because launch does not have yield, this has type
952
+ # Callable[[], Coroutine[Any, Any, AsyncIterator[int]]]
953
+ # instead of
954
+ # Callable[[], AsyncIterator[int]]
955
+ async def launch (self ) -> AsyncIterator[int ]:
956
+ raise NotImplementedError
957
+
958
+ class LauncherCorrect (Protocol ):
959
+ def launch (self ) -> AsyncIterator[int ]:
960
+ raise NotImplementedError
945
961
946
- loop = asyncio.get_event_loop()
947
- loop.run_until_complete(countdown_2(" USS Enterprise" , 5 ))
948
- loop.close()
962
+ class LauncherAlsoCorrect (Protocol ):
963
+ async def launch (self ) -> AsyncIterator[int ]:
964
+ raise NotImplementedError
965
+ if False :
966
+ yield 0
0 commit comments