8.12. Asynchronní kontextové manažery

Přehled Pythonu představil kontextové manažery – objekty, které řídí bloky with, kde se __enter__ provádí při vstupu a __exit__ při výstupu bez ohledu na to, jak blok skončí. Asynchronní kontextové manažery jsou tatáž myšlenka přenesená do asyncio: __aenter__ a __aexit__ jsou korutiny, takže příprava nebo úklid mohou na něco awaitovat. Blok, který je řídí, je async with.

Jeden jsme již použili. asyncio.Lock je asynchronní kontextový manažer; stránka o zámcích jej použila jako async with bus_lock: .... Tato stránka je o napsání vlastního pro prostředek aplikace.

8.12.1. Kdy si jej napsat

Když má aplikace prostředek, který potřebuje párovou přípravu a uvolnění, a alespoň jedna z těchto stran musí na něco awaitovat. Síťová spojení, senzory, které po konfiguraci potřebují čas na ustálení, cokoli, co něco zamkne při vstupu a odemkne při výstupu. Prostá synchronní podoba with se nepoužije, protože její __enter__ a __exit__ nemohou být korutinami.

8.12.2. Dvě metody

  • async def __aenter__(self) se spustí při vstupu do bloku. Cokoli vrátí, je to, k čemu se naváže volitelná klauzule as name u async with. Vracení self je nejběžnější podobou, ale funguje libovolná hodnota.

  • async def __aexit__(self, exc_type, exc, tb) se spustí při výstupu z bloku. exc_type je None při normálním výstupu; při výjimce (nebo zrušení) je to třída výjimky a exc je instance. Vrácení pravdivé hodnoty řekne Pythonu, že výjimka byla ošetřena a neměla by se propagovat. Vrácení None (obvyklý případ) nechá výjimku po proběhnutí úklidu pokračovat nahoru řetězcem volání.

8.12.3. Rozpracovaný příklad

Wrapper reflektoru, který po dobu těla bloku rozsvítí LED, s krátkým časem na ustálení, aby bylo osvětlení před jakýmkoli snímáním stabilní, a při výstupu LED opět zhasne:

import asyncio
from machine import LED


class Spotlight:
    def __init__(self, led):
        self._led = led

    async def __aenter__(self):
        self._led.on()
        await asyncio.sleep_ms(50)
        return self

    async def __aexit__(self, exc_type, exc, tb):
        self._led.off()

async def main():
    led = LED("LED_WHITE")

    async with Spotlight(led):
        # work that benefits from steady illumination
        await asyncio.sleep_ms(200)

asyncio.run(main())

Při vstupu do bloku se spustí __aenter__: LED se rozsvítí, 50ms await na ustálení předá řízení smyčce, takže mezitím mohou postupovat ostatní korutiny, a tělo bloku se spustí, jakmile čekání skončí. Když blok skončí – při normálním výstupu, při výjimce nebo při zrušení – spustí se __aexit__ a LED se opět zhasne. Úklid proběhne v každém případě; to je záruka, kterou async with poskytuje.

The frame capture page shows how to make csi.CSI.snapshot() await-friendly; once that wrapper is in hand, the body of an async with Spotlight(led): block would typically be a capture loop running under the steady illumination.