formations/python-avancé/6-coro.md

3.5 KiB

async / await

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

async def pong_handler():
    client.write(await client.read())
    await client.flush()
    client.close()

Les coroutines

  • generator-based coroutines
  • native coroutines

Generator-based coroutines

import types


@types.coroutine
def get_then_print(url):
    ...

Native coroutines

async def get_then_print(url):
    ...

Coroutines

Une coroutine, renvoie un objet coroutine :

>>> async def tum():
...     print("tum")
...
>>> tum()
<coroutine object tum at 0x7fa294538468>

Coroutines

>>> async def tum():
...     print("tum")
...
>>> a_coroutine_object = tum()
>>> a_coroutine_object.send(None)
tum
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  StopIteration

Notes:

qu'on peut manipuler.

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).

Récupérer un résultat

Le résultat d'une coroutine est stocké dans l'exception StopIteration.

Notes:

Dans l'attribut value.

await

async def two():
    return 2

async def four():
    return await two() + await two()

coro = four()
coro.send(None)

Notes:

Ça donne StopIteration: 4, de manière complètement synchrone.

Suspendre une coroutine.

Ce n'est pas possible dans une coroutine.

Notes:

Bon, à part await asyncio.sleep(0), ou toute attente vers un awaitable qui se suspend sans rien faire.

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(None).

Awaitables

Les awaitables sont des objets pouvant être « attendus » via un await.

Notes:

Typiquement coroutine ou un objet implémentant __await__.

Gérer ses coroutines

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

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

def frenetic_coro_manager(coro):
    try:
        while True:
            coro.send(None)
    except StopIteration as stop:
        return stop.value

Gérer ses coroutines

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

async def tum():
    for _ in range(10):  # ou : while True:
        await Awaitable()
        print("Tum")

async def pak():
    for _ in range(10):  # ou : while True:
        await Awaitable()
        print("Pak")

frenetic_coros_manager(tum(), pak())