8.12. Gestores de contexto asíncronos¶
La Introducción a Python presentó los gestores de contexto – los objetos que controlan los bloques with, con __enter__ ejecutándose a la entrada y __exit__ ejecutándose a la salida sin importar cómo termine el bloque. Los gestores de contexto asíncronos son la misma idea trasladada a asyncio: __aenter__ y __aexit__ son corrutinas, de modo que la configuración o la limpieza pueden hacer await de algo. El bloque que los controla es async with.
Ya hemos usado uno. asyncio.Lock es un gestor de contexto asíncrono; la página de locks lo usó como async with bus_lock: .... Esta página trata sobre escribir uno para el propio recurso de una aplicación.
8.12.1. Cuándo escribir uno¶
Cuando la aplicación tiene un recurso que necesita configuración y desmontaje emparejados, y al menos uno de esos lados tiene que hacer await de algo. Conexiones de red, sensores que necesitan un retardo de estabilización tras la configuración, cualquier cosa que bloquee algo a la entrada y lo desbloquee a la salida. La forma síncrona simple with no aplica porque sus __enter__ y __exit__ no pueden ser corrutinas.
8.12.2. Los dos métodos¶
async def __aenter__(self)se ejecuta cuando se entra en el bloque. Lo que retorna es aquello a lo que se vincula la cláusula opcionalas namedeasync with. Retornarselfes la forma más común, pero cualquier valor funciona.async def __aexit__(self, exc_type, exc, tb)se ejecuta cuando se sale del bloque.exc_typeesNoneen una salida normal; en una excepción (o una cancelación) es la clase de la excepción, yexces la instancia. Retornar un valor verdadero le dice a Python que la excepción ha sido manejada y no debe propagarse. RetornarNone(el caso habitual) deja que la excepción continúe hacia arriba por la cadena de llamadas después de que se ejecute la limpieza.
8.12.3. Un ejemplo trabajado¶
Un envoltorio de foco que enciende un LED durante el cuerpo del bloque, con un breve retardo de estabilización para que la iluminación sea estable antes de que ocurran las capturas, y vuelve a apagar el LED a la salida:
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())
Cuando se entra en el bloque, se ejecuta __aenter__: el LED se enciende, el await de estabilización de 50 ms cede el control al bucle para que otras corrutinas puedan avanzar mientras tanto, y el cuerpo del bloque comienza una vez que la espera se completa. Cuando el bloque termina – en una salida normal, en una excepción o en una cancelación – se ejecuta __aexit__ y el LED se vuelve a apagar. La limpieza se ejecuta en todos los casos; esa es la garantía que async with proporciona.
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.