web-dev-qa-db-ja.com

python-asyncio TypeError: 'await'式でオブジェクト辞書を使用できません

サードパーティのモジュールを使用して、APIからデータを取得しています。私は単にモジュールが非同期でデータを返すのを待ちたいと思いますが、それは時々数秒かかり、アプリをフリーズさせます。ただし、そのモジュールの呼び出しを待機しようとすると、TypeErrorが表示されます。

TypeError: object dict can't be used in 'await' expression

import thirdPartyAPIwrapper

async def getData():
    retrienveData = await thirdPartyAPIWrapper.data()
    return await retrieveData

def main():
    loop = asncio.get_event_loop()
    data = loop.run_until_complete(getData())
    loop.close
    return data

なぜtype( 'dict')を待てないのですか?これを回避する方法はありますか? asyncioを使用したasync/awaitがコルーチンを返さないサードパーティモジュールで動作しない場合、他のオプションは何ですか?

11
Riley Hughes

非同期(async defで定義)関数のみが待機できます。全体的な考えは、そのような関数は、イベントループをブロックせずに実行(await)できるようにする特別な方法で記述されるということです。

実行にかなりの時間を要する一般的な(defで定義された)関数から結果を取得する場合、次のオプションがあります。

  • この関数全体を非同期に書き換えます
  • この関数を別のスレッドで呼び出し、非同期的に結果を待ちます
  • 別のプロセスでこの関数を呼び出し、非同期的に結果を待ちます

通常、2番目のオプションを選択します。

これを行う方法の例を次に示します。

import asyncio
import time
from concurrent.futures import ThreadPoolExecutor


_executor = ThreadPoolExecutor(1)


def sync_blocking():
    time.sleep(2)


async def hello_world():
    # run blocking function in another thread,
    # and wait for it's result:
    await loop.run_in_executor(_executor, sync_blocking)


loop = asyncio.get_event_loop()
loop.run_until_complete(hello_world())
loop.close()

Asyncioの仕組みについて この回答 をお読みください。私はそれが大いに役立つと思います。

16

thirdPartyAPIWrapper.data()は通常の同期関数なので、別のスレッドで呼び出す必要があります。

asgirefライブラリにはそのためのヘルパー関数があります。
引数付きのブロッキング関数があると仮定します。

import asyncio
import time

from asgiref.sync import sync_to_async


def blocking_function(seconds: int) -> str:
    time.sleep(seconds)
    return f"Finished in {seconds} seconds"

async def main():
    seconds_to_sleep = 5
    function_message = await sync_to_async(blocking_function)(seconds_to_sleep)
    print(function_message)

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()

そのライブラリにはasync_to_syncヘルパー関数もあります。

1
Serge