web-dev-qa-db-ja.com

コールバックを含むPython関数をasyncio待機可能に変換する

PyAudioライブラリを非同期コンテキストで使用したいのですが、ライブラリのメインエントリポイントにはコールバックベースのAPIしかありません。

_import pyaudio

def callback(in_data, frame_count, time_info, status):
    # Do something with data

pa = pyaudio.PyAudio()
self.stream = self.pa.open(
    stream_callback=callback
)
_

私がそれを使用したいと思っている方法は次のようなものです:

_pa = SOME_ASYNC_COROUTINE()
async def listen():
    async for block in pa:
        # Do something with block
_

問題は、このコールバック構文を、コールバックが発生したときに完了するフューチャーに変換する方法がわからないことです。 JavaScriptでは promise.promisify() を使用しますが、Pythonにはそのようなものはないようです。

10
Migwell

Future を使用することもできます

クラスasyncio.Future(*、loop = None)¶

Futureは、非同期操作の最終的な結果を表します。スレッドセーフではありません。

未来は待ち望まれるオブジェクトです。コルーチンは、結果または例外が設定されるか、キャンセルされるまで、Futureオブジェクトを待機できます。

通常、Futureは低レベルのコールバックベースのコード(asyncioトランスポートを使用して実装されたプロトコルなど)を高レベルの非同期/待機コードと相互運用できるようにするために使用されます。

経験則では、ユーザー向けAPIでFutureオブジェクトを公開しないでください。Futureオブジェクトを作成するための推奨方法は、loop.create_future()を呼び出すことです。このようにして、代替イベントループ実装は、Futureオブジェクトの独自の最適化された実装を注入できます。

ばかげた例:

def my_func(loop):
    fut = loop.create_future()
    pa.open(
        stream_callback=lambda *a, **kw: fut.set_result([a, kw])
    )
    return fut


async def main(loop):
    result = await my_func(loop)  # returns a list with args and kwargs 

pa.openは、スレッドまたはサブプロセスで実行されます。そうでない場合は、openへの呼び出しを asyncio.loop.run_in_executor でラップする必要がある場合もあります。

1