8.12. Async context managers¶
Het Python-overzicht introduceerde context managers – de objecten die with-blokken aansturen, waarbij __enter__ draait bij het binnenkomen en __exit__ draait bij het verlaten, ongeacht hoe het blok eindigt. Async context managers zijn hetzelfde idee, verplaatst naar asyncio: __aenter__ en __aexit__ zijn coroutines, zodat de setup of de opschoning iets kan awaiten. Het blok dat ze aanstuurt is async with.
We hebben er al een gebruikt. asyncio.Lock is een async context manager; de pagina locks gebruikte het als async with bus_lock: .... Deze pagina gaat over het schrijven van er een voor een eigen resource van een applicatie.
8.12.1. Wanneer je er een moet schrijven¶
Wanneer de applicatie een resource heeft die gepaarde setup en teardown nodig heeft, en ten minste een van die kanten iets moet awaiten. Netwerkverbindingen, sensoren die een instabilisatievertraging nodig hebben na configuratie, alles wat iets vergrendelt bij binnenkomst en ontgrendelt bij vertrek. De gewone synchrone with-vorm is niet van toepassing omdat zijn __enter__ en __exit__ geen coroutines kunnen zijn.
8.12.2. De twee methoden¶
async def __aenter__(self)draait wanneer het blok wordt binnengekomen. Wat het ook retourneert, is waar de optioneleas name-clausule vanasync withaan bindt.selfretourneren is de meest voorkomende vorm, maar elke waarde werkt.async def __aexit__(self, exc_type, exc, tb)draait wanneer het blok wordt verlaten.exc_typeisNonebij een normaal vertrek; bij een exceptie (of een annulering) is het de klasse van de exceptie, enexcis de instantie. Het retourneren van een waarheidsgetrouwe waarde vertelt Python dat de exceptie is afgehandeld en niet mag propageren. Het retourneren vanNone(het gebruikelijke geval) laat de exceptie verdergaan omhoog door de aanroepketen nadat de opschoning is gedraaid.
8.12.3. Een uitgewerkt voorbeeld¶
Een spotlight-wrapper die een LED aanzet voor de duur van het blok, met een korte instabilisatievertraging zodat de verlichting stabiel is voordat er beelden worden vastgelegd, en de LED weer uitzet bij het verlaten:
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())
Wanneer het blok wordt binnengekomen, draait __aenter__: de LED gaat aan, de await van 50 ms instabilisatie geeft de controle terug aan de loop zodat andere coroutines ondertussen voortgang kunnen maken, en de body van het blok start zodra het wachten is voltooid. Wanneer het blok eindigt – bij een normaal vertrek, bij een exceptie of bij annulering – draait __aexit__ en gaat de LED weer uit. De opschoning draait in elk geval; dat is de garantie die async with biedt.
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.