## asyncio (ou pas)
## Julien Palard - Software Engineer - cPython core dev - Coordinator of docs.python.org/fr/ - Python trainer - hackinscience.org
## Coroutine Une coroutine est une fonction dont l'exécution peut être suspendue.
## Callback Hell ``` function pong_handler(client) { client.on('data', function (data) { client.on('data_written', function () { client.close() }); client.write(data) client.flush() }); } ```
## Avec des coroutines ```python async def pong_handler(): client.write(await client.read()) await client.flush() client.close() ```
## Les coroutines Pour être exhaustif, il existe deux types de coroutines en Python : - generator-based coroutines - native coroutines
## Generator-based coroutines ```pytho import types @types.coroutine def get_then_print(url): ... ```
## Native coroutines ```python async def get_then_print(url): ... ```
## Coroutines Une `coroutine`, renvoie un objet `coroutine` : ``` >>> async def tum(): ... print("tum") ... >>> tum()
```
## Coroutines Qu'on peut manipuler : ``` >>> 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 ``` Notes: As you can see, calling `tum()` did not execute the `print("tum")`, but calling `.send(None)` did (see PEP 342). L'appel de .send est fait par la main loop (asyncio.run).
## Coroutines Une coroutine renvoie juste un objet coroutine, donc à un moment, du code devra appeler la méthode `send` dessus. C'est le rôle d'une *main loop*.
## Récupérer un résultat Le résultat d'une coroutine est stocké dans l'exception `StopIteration`. Notes: Dans l'attribut `value`.
## await Il existe un mot clé dédié à la récupération de résultat d'une coroutine : `await`
## await Lorsqu'on lui donne un *awaitable*, `await` essaye d'obtenir son résultat. Si l'*awaitable* se suspend, `await` suspends à son tour la coroutine actuelle, récursivemet, jusqu'à l'appel à `send`.
## await ``` async def two(): return 2 async def four(): return await two() + await two() coro = four() coro.send(None) ``` Ce qui donne `StopIteration: 4`, de manière complètement synchrone, malgré le vocabulaire utilisé.
## Suspendre une coroutine. Ce n'est pas possible dans une coroutine. Mais toute la chaîne d'*await* peut-être suspendue en utilisant un `yield` depuis un *future-like object*.
## Future-like object Un `future-like object` est un object implémentant `__await__`, qui a le droit de `yield`. L'expression du yield traversera toute la stack d'`await` jusqu’au `send`.
## Awaitables Les [awaitables](https://www.python.org/dev/peps/pep-0492/#await-expression) sont des objets pouvant être « attendus » via un `await`. Donc c'est soit une `coroutine` soit un objet implémentant `__await__`.
## Gérer ses coroutines Appelons simplement `send`, et attendons-nous à la fameuse `StopIteration` : ```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())) ```
## Gérer ses coroutines Mais si la coroutine se suspend, notre `coro_manager` ne suffira pas : ```python class Awaitable: def __await__(self): yield async def wont_terminate_here(): await Awaitable() print("Terminated") return 42 print(coro_manager(wont_terminate_here())) ```
## Gérer ses coroutines Donc, un gestionnaire de coroutines doit rappeler send, comme celui-ci : ```python def frenetic_coro_manager(coro): try: while True: coro.send(None) except StopIteration as stop: return stop.value ```
## Gérer ses coroutines Mais notre manager frénétique ne sait s'occuper que d'une seule routine, peut-on faire mieux ?
## Gérer ses coroutines On pourrait relancer le travail de l'une d'entre elles au hasard : ```python import random 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) ```
## Gérer ses coroutines Essayons : ```python async def tum(): while True: await Awaitable() print("Tum") async def pak(): while True: await Awaitable() print("Pak") frenetic_coros_manager(tum(), pak()) ``` Ohh, deux `while True:` qui coopèrent !
## Questions? - mamot.fr/@mdk - github.com/JulienPalard - mdk on #python-fr on libera.chat