コンテンツにスキップ

はじめに

動作要件

  • Python 3.10 以降(CPython または PyPy)
  • サードパーティ依存なし

インストール

Warning

PyPI にはまだ公開されていません。GitHub からインストールしてください。

pip install git+https://github.com/jrfk/asyncio-util

uv を使う場合:

uv add git+https://github.com/jrfk/asyncio-util

はじめての AsyncValue

AsyncValue は初期値をラップします。読み書きは value プロパティを通して、ふつうの属性と同じように行います。

from asyncio_util import AsyncValue

score = AsyncValue(0)
print(score.value)   # 0
score.value = 10
print(score.value)   # 10

ふつうの属性との違いは、他のタスクが await できることです。

import asyncio
from asyncio_util import AsyncValue

score = AsyncValue(0)

async def announcer():
    value = await score.wait_value(lambda v: v >= 100)
    print(f"{value} に到達!")

async def game():
    for points in (30, 40, 50):
        await asyncio.sleep(0.1)
        score.value += points   # プロパティは累算代入にも対応

async def main():
    await asyncio.gather(announcer(), game())

asyncio.run(main())

出力:

120 に到達!

ここにポーリングはありません。announcer()score.value += points の代入で 述語が真になるまで眠っていて、条件を満たしたまさにその値を受け取ります。

値と述語

待機系の API はすべて、ふつうの値(== で比較)と述語関数の どちらでも受け付けます。

await score.wait_value(100)                  # value == 100
await score.wait_value(lambda v: v >= 100)   # predicate(value) が True

None を待つ

None も他の値と同じように扱われます。 await av.wait_value(None) は値が None になるまで待ちます。

タイムアウト

wait_value()wait_transition()timeout 引数を受け取り、期限切れで asyncio.TimeoutError を送出します。

try:
    await score.wait_value(100, timeout=1.0)
except asyncio.TimeoutError:
    print("タイムアウト!")

標準ライブラリのイディオムとの組み合わせも同様に機能します。コードベースの 流儀に合わせてください。

await asyncio.wait_for(score.wait_value(100), timeout=1.0)   # 3.10+

async with asyncio.timeout(1.0):                             # 3.11+
    await score.wait_value(100)

タイムアウトなどで待機がキャンセルされた場合、内部のウェイターは即座に 片付けられます。リークはありません。

等値性について

現在の値と等しい(==)値の代入は何もしません。ウェイターは起きず、 遷移としても記録されません。この重複排除があるおかげで、派生値 (派生と合成を参照)を低コストに保てます。

スレッド安全性

AsyncValueスレッドセーフではありませんvalue への代入は イベントループが動いているスレッドからのみ行ってください。別スレッドからは、 まずループに乗せます。

loop.call_soon_threadsafe(setattr, av, "value", 42)

次のステップ