## asyncio (ou pas)
## Julien Palard - Software Engineer - cPython core dev - Coordinator of docs.python.org/fr/ - Python trainer - hackinscience.org
## Coroutine A [coroutine](https://en.wikipedia.org/wiki/Coroutine) is a function whose execution can be suspended.
## Callback Hell ``` function pong_handler(client) { client.on('data', function (data) { client.on('data_written', function () { client.close() }); client.write(data) client.flush() }); } ```
## With coroutines ``` async function pong_handler() { client.write(await client.read()) await client.flush() client.close() } ```
## Coroutines in Python To be exhaustive, there are two kinds of coroutines in Python: - generator-based coroutines - native coroutines
## Generator-based coroutines ```python @types.coroutine def get_then_print(url): ... ```
## Native coroutines ```python async def get_then_print(url): ... ```
## Native coroutines A `coroutine function`, when called returns a `coroutine object`: ```python >>> async def tum(): ... print("tum") ... >>> tum()
```
## Native coroutines Which can be manipulated: ```pycon >>> async def tum(): ... print("tum") ... >>> a_coroutine_object = tum() >>> a_coroutine_object.send(None) tum Traceback (most recent call last): File "
", line 1, in
StopIteration ``` As you can see, calling `tum()` did not execute the `print("tum")`, but calling `.send(None)` did (see PEP 342).
## Native coroutines Also, `send` told us the coroutine is complete by throwing a `StopIteration`.
## Coroutine invocation As a coroutine just return a coroutine object when called, at some point, some code have to call `send(None)` on it. But that's typically the role of a "main loop".
## Getting result from a coroutine call A coroutine give its returned value in a `StopIteration` exception, meaning "Job is done, stop trying to un-suspend me".
## await There's also a keyword dedicated to pull a value from a coroutine: `await`. Given an `awaitable`, `await` tries to get a value from it, and if the `awaitable` suspends, `await` also suspends the current coroutine, and so on, up to the `.send()` caller.
## await ```python async def two(): return 2 async def four(): return await two() + await two() coro = four() coro.send(None) ``` Which gets `StopIteration: 4`, in a completly synchronous way, despite the vocabulary used.
## Suspending a coroutine This is just not possible for a coroutine to suspend itself. But the whole chain of `await` can be suspended using a `yield` in a "future-like object".
## Future-like object A `future-like` object is an object with an `__await__` method, which can `yield` a value which will traverse the whole `await` chain up to the caller of `.send(None)`.
## Awaitables [awaitables](https://www.python.org/dev/peps/pep-0492/#await-expression) are objects that can be `awaited` with an `await` keyword. Basically a `coroutine` or an object with an `__await__` method returning an iterator are both awaitables.
## Managing coroutines The first step for a coroutine manager is probably to call `send(None)`, which is enough to do some work: ```python async def two(): return 2 async def four(): return await two() + await two() def coro_manager(coro): try: coro.send(None) except StopIteration as stop: return stop.value print(coro_manager(four())) ```
## Managing coroutines But this `coro_manager` will just don't terminate the work if a suspension occurs: ```python class Awaitable: def __await__(self): yield async def wont_terminate_here(): await Awaitable() print("Terminated") return 42 print(coro_manager(wont_terminate_here())) # "Terminated" is not printed ```
## Managing coroutines So a `coro_manager` should probably restore a suspended coroutine, by calling back `send(None)`, like this `frenetic_coro_manager`: ```python def frenetic_coro_manager(coro): try: while True: coro.send(None) except StopIteration as stop: return stop.value ```
## Managing coroutines But here, this `frenetic_coro_manager` can only execute a single coroutine. What if there's more?
## Managing coroutines Let's restore a random one: ```python def frenetic_coros_manager(*coros): coros = list(coros) while coros: coro = random.choice(coros) try: coro.send(None) except StopIteration as stop: coros.remove(coro) ```
## Managing coroutines Let's try it: ```python async def tum(): while True: await Awaitable() print("Tum") async def pak(): while True: await Awaitable() print("Pak") frenetic_coro_manager(tum(), pak()) ``` This time, this frenetically print "Tum"s and "Pak"s. Both `while True` cooperated!
## Questions? - mamot.fr/@mdk - github.com/JulienPalard - mdk on #python-fr on libera.chat