8.12. Gerenciadores de contexto assíncronos

A Visão Geral do Python apresentou os gerenciadores de contexto – os objetos que os blocos with controlam, com __enter__ executando na entrada e __exit__ executando na saída, não importa como o bloco termine. Os gerenciadores de contexto assíncronos são a mesma ideia transposta para o asyncio: __aenter__ e __aexit__ são corrotinas, de modo que a configuração ou a limpeza pode await algo. O bloco que os controla é async with.

Já usamos um. asyncio.Lock é um gerenciador de contexto assíncrono; a página locks o usou como async with bus_lock: .... Esta página trata de escrever um para o recurso próprio de uma aplicação.

8.12.1. Quando escrever um

Quando a aplicação tem um recurso que precisa de configuração e desmontagem pareadas, e pelo menos um desses lados precisa await algo. Conexões de rede, sensores que precisam de um atraso de estabilização após a configuração, qualquer coisa que trave algo na entrada e destrave na saída. O formato síncrono simples de with não se aplica porque seus __enter__ e __exit__ não podem ser corrotinas.

8.12.2. Os dois métodos

  • async def __aenter__(self) é executado quando o bloco é iniciado. O que quer que ele retorne é o que a cláusula opcional as name do async with vincula. Retornar self é o formato mais comum, mas qualquer valor funciona.

  • async def __aexit__(self, exc_type, exc, tb) é executado quando o bloco é encerrado. exc_type é None em uma saída normal; em uma exceção (ou um cancelamento), é a classe da exceção, e exc é a instância. Retornar um valor verdadeiro informa ao Python que a exceção foi tratada e não deve se propagar. Retornar None (o caso usual) deixa a exceção continuar pela cadeia de chamadas depois que a limpeza é executada.

8.12.3. Um exemplo prático

Um wrapper de holofote que liga um LED durante o corpo do bloco, com um curto atraso de estabilização para que a iluminação fique estável antes que qualquer captura aconteça, 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__ é executado: o LED acende, o await de estabilização de 50 ms cede o controle ao laço para que outras corrotinas possam avançar enquanto isso, e o corpo do bloco começa assim que a espera termina. Quando o bloco termina – em uma saída normal, em uma exceção ou em um cancelamento – __aexit__ é executado e o LED volta a desligar. A limpeza é executada em todos os casos; essa é a garantia que o 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.