@@ -824,11 +824,11 @@ classes are generic, self-type allows giving them precise signatures:
824824Typing async/await
825825******************
826826
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 >`_ .
830830
831- Functions defined using ``async def `` are typed just like normal functions.
831+ Functions defined using ``async def `` are typed similar to normal functions.
832832The return type annotation should be the same as the type of the value you
833833expect to get back when ``await ``-ing the coroutine.
834834
@@ -839,65 +839,40 @@ expect to get back when ``await``-ing the coroutine.
839839 async def format_string (tag : str , count : int ) -> str :
840840 return f ' T-minus { count} ( { tag} ) '
841841
842- async def countdown_1 (tag : str , count : int ) -> str :
842+ async def countdown (tag : str , count : int ) -> str :
843843 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
845845 print (my_str)
846846 await asyncio.sleep(0.1 )
847847 count -= 1
848848 return " Blastoff!"
849849
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 ))
853851
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
856855:py:class: `Awaitable[T] <typing.Awaitable> `:
857856
858857.. code-block :: python
859858
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]"
862861
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 :
874863
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+ ----------------------
879866
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:
894869
895870.. code-block :: python
896871
897872 from typing import Optional, AsyncIterator
898873 import asyncio
899874
900- class arange (AsyncIterator[ int ]) :
875+ class arange :
901876 def __init__ (self , start : int , stop : int , step : int ) -> None :
902877 self .start = start
903878 self .stop = stop
@@ -914,35 +889,78 @@ To create an iterable coroutine, subclass :py:class:`~typing.AsyncIterator`:
914889 else :
915890 return self .count
916891
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 :
919894 print (f ' T-minus { i} ( { tag} ) ' )
920895 await asyncio.sleep(0.1 )
921896 return " Blastoff!"
922897
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 )))
926899
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:
932902
933903.. code-block :: python
934904
935- from typing import Any, Generator
905+ from typing import AsyncGenerator, Optional
936906 import asyncio
937907
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
945961
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