8.12. 非同期コンテキストマネージャ

Python 概要 ではコンテキストマネージャを紹介しました -- with ブロックが駆動するオブジェクトで、入る際に __enter__ が実行され、ブロックがどのように終わろうとも出る際に __exit__ が実行されます。非同期コンテキストマネージャは、同じ考え方を asyncio に持ち込んだものです。__aenter____aexit__コルーチン なので、セットアップやクリーンアップが何かを await できます。それらを駆動するブロックは async with です。

私たちはすでに1つ使っています。asyncio.Lock は非同期コンテキストマネージャであり、ロック ページではそれを async with bus_lock: ... として使いました。このページは、アプリケーション自身のリソース向けにそれを書くことについてです。

8.12.1. いつ書くか

アプリケーションが 対になった セットアップとティアダウンを必要とするリソースを持ち、そのどちらか一方が何かを await しなければならない場合です。ネットワーク接続、設定後に安定待ちの遅延が必要なセンサー、入る際に何かをロックし出る際にアンロックするものなどです。素の同期型の with の形は当てはまりません。その __enter____exit__ はコルーチンにできないからです。

8.12.2. 2つのメソッド

  • async def __aenter__(self) はブロックに入ったときに実行されます。それが返すものが、async with の省略可能な as name 節がバインドする対象になります。self を返すのが最も一般的な形ですが、どんな値でも機能します。

  • async def __aexit__(self, exc_type, exc, tb) はブロックを出るときに実行されます。exc_type は正常に出る場合は None です。例外(またはキャンセル)の場合はその例外のクラスであり、exc はそのインスタンスです。真と評価される値を返すと、例外が 処理された ので伝播すべきではないと Python に伝えます。None を返すと(通常のケース)、クリーンアップの実行後に例外が呼び出しチェーンを上へと進み続けます。

8.12.3. 実践的な例

ブロックの本体の間だけLEDを点灯させるスポットライトのラッパーです。キャプチャが行われる前に照明が安定するよう短い安定待ちの遅延を設け、抜ける際には再びLEDを消灯します:

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())

ブロックに入ると __aenter__ が実行されます。LEDが点灯し、50 ms の安定待ちの await がループに制御を譲るので、その間に他のコルーチンが進行でき、待機が完了するとブロックの本体が開始します。ブロックが終わると -- 正常に出る場合でも、例外の場合でも、キャンセルの場合でも -- __aexit__ が実行され、LEDが再び消灯します。クリーンアップはどの場合でも実行されます。それが async with が提供する保証です。

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.