web-dev-qa-db-fra.com

python-asyncio TypeError: la définition d'objet ne peut pas être utilisée dans l'expression 'wait'

J'utilise un module tiers pour récupérer les données d'une API. Je voudrais simplement attendre de manière asynchrone le module pour renvoyer les données, ce qui prend parfois plusieurs secondes et gèle mon application. Cependant, lorsque j'essaye d'attendre un appel de ce module, je reçois le 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

Pourquoi ne puis-je pas attendre un type ('dict')? Y a-t-il un moyen de contourner cela? Si async/wait with asyncio ne fonctionnera pas avec un module tiers qui ne renvoie pas de coroutine, quelles sont mes autres options?

4
Riley Hughes

Seules des fonctions asynchrones (définies avec async def) peuvent être attendues. L’idée générale est que de telles fonctions sont écrites de manière spéciale, ce qui permet de les exécuter (await) sans bloquer la boucle d’événement.

Si vous voulez obtenir le résultat d'une fonction commune (définie avec def) dont l'exécution prend beaucoup de temps, vous avez les options suivantes:

  • réécrire toute cette fonction pour être asynchrone
  • appeler cette fonction dans un autre thread et attendre le résultat de manière asynchrone
  • appeler cette fonction dans un autre processus et attendre le résultat de manière asynchrone

Habituellement, vous voulez choisir une deuxième option. 

Voici un exemple de la façon de le faire:

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

S'il vous plaît, lisez cette réponse sur le fonctionnement de l'asyncio. Je pense que ça vous aidera beaucoup.

5
Mikhail Gerasimov

Comme thirdPartyAPIWrapper.data() est une fonction de synchronisation normale, vous devriez l'appeler dans un autre thread.

Il existe une fonction d'assistance pour cela dans une bibliothèque asgiref.
Supposons que nous ayons une fonction de blocage avec un argument:

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

Il existe également une fonction d'assistance async_to_sync dans cette bibliothèque.

0
Serge