8.12. Gestores de contexto assíncronos¶
A Visão Geral do Python apresentou os gestores de contexto – os objetos que os blocos with utilizam, com __enter__ a executar na entrada e __exit__ a executar na saída independentemente de como o bloco termina. Os gestores de contexto assíncronos são a mesma ideia aplicada ao asyncio: __aenter__ e __aexit__ são coroutines, pelo que a configuração ou a limpeza podem usar await. O bloco que os aciona é async with.
Já usámos um. asyncio.Lock é um gestor de contexto assíncrono; a página de locks utilizou-o como async with bus_lock: .... Esta página é sobre como escrever um para um recurso próprio da aplicação.
8.12.1. Quando escrever um¶
Quando a aplicação tem um recurso que precisa de configuração e limpeza emparelhadas, e pelo menos um desses lados tem de usar await. Ligações de rede, sensores que precisam de um atraso de estabilização após a configuração, qualquer coisa que bloqueie algo na entrada e desbloqueie na saída. A forma síncrona simples with não se aplica porque os seus __enter__ e __exit__ não podem ser coroutines.
8.12.2. Os dois métodos¶
async def __aenter__(self)executa quando o bloco é iniciado. O que quer que devolva é o que a cláusula opcionalas namedeasync withfica a referenciar. Devolverselfé a forma mais comum, mas qualquer valor funciona.async def __aexit__(self, exc_type, exc, tb)executa quando o bloco termina.exc_typeéNonenuma saída normal; numa exceção (ou num cancelamento) é a classe da exceção, eexcé a instância. Devolver um valor verdadeiro diz ao Python que a exceção foi tratada e não deve propagar-se. DevolverNone(o caso habitual) deixa a exceção continuar a subir na cadeia de chamadas após a execução da limpeza.
8.12.3. Um exemplo prático¶
Um invólucro de holofote que liga um LED para o corpo do bloco, com um pequeno atraso de estabilização para que a iluminação esteja estável antes de qualquer captura, e desliga o LED novamente na saída:
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())
Quando o bloco é iniciado, __aenter__ executa: o LED acende-se, os 50 ms de estabilização com await cedem o controlo ao loop para que outras coroutines possam progredir entretanto, e o corpo do bloco começa assim que a espera termina. Quando o bloco termina – numa saída normal, numa exceção, ou num cancelamento – __aexit__ executa e o LED apaga-se novamente. A limpeza é executada em todos os casos; é essa a garantia que async with oferece.
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.