0

I am new with async functions and i'm trying to make multiple calls from an external API. concurrent.Futures is not quite enough to retrieve the responses so i tried with asyncio and httpx but the process is throwing an error unknown and difficult to debug for me.

It seems that the coroutine is having an empty value or is never being called.

This is my client

async def get_product_by_id_async(self, product_id: str) -> Response:
        if product_id is None:
            return None
        response_await = await self.async_client.get(
            url=f"{self.base_url}/api/catalog/stock{product_id}",
            headers=self.headers,
        )

        if response_await.status_code == 200:
            response = json.loads(response_await.text)
            return response

And this is my coordinating function


async def async_get_products(data_parser):
    path = data_parser.options["path"]
    sku_list = client.run()
    products_list = []
    tasks = [await client.get_product_by_id(sku) for sku in sku_list]
    breakpoint()
    completed = await asyncio.gather(*tasks)
    for product in completed:
        products_list = build_product_df(products_list, product)
    products_df = pd.DataFrame(products_list)
    products_df.to_csv(path, index=False)
    return products_df

def products_processor_concurrent(data_parser):
    return async_get_products(data_parser)

After the solution given there is a new error in the code: This is the trace:

Traceback (most recent call last):
  File "manage.py", line 32, in <module>
    module.run()
  File "/opt/project/providers/api/data_collector.py", line 90, in run
    integrator.run()
  File "/opt/project/common/integrator.py", line 448, in run
    self.start_processing()
  File "/opt/project/providers/api/data_collector.py", line 81, in start_processing
    products.run()
  File "/opt/project/common/data_parser.py", line 94, in run
    self.build_dataframe()
  File "/opt/project/common/data_parser.py", line 54, in build_dataframe
    df = func(self)
  File "/opt/project/providers/data_collector.py", line 68, in products_processor_concurrent
    return run(async_get_products(data_parser))
  File "/usr/local/lib/python3.8/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/local/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
    return future.result()
  File "/opt/project/providers/api/data_collector.py", line 57, in async_get_products
    tasks = [await client.get_product_by_id(sku) for sku in sku_list]
  File "/opt/project/providers/api/data_collector.py", line 57, in <listcomp>
    tasks = [await client.get_product_by_id(sku) for sku in sku_list]
TypeError: object dict can't be used in 'await' expression
bittrago
  • 31
  • 1
  • 6
  • I can understand why the error is thrown, but I'm not sure what the syntax would be to fix it. For each ```sku``` in ```sku_list```, you are using ```sku``` as the parameter in your ```get_product_by_id_async``` method, which returns a dictionary. It seems like the program is running the function without needing the ```await``` in front of it, because it runs the function, gets the dictionary, and then tries to await the result of the function, instead of awaiting the function itself. I would recommend removing await: ```tasks = [client.get_product_by_id(sku) for sku in sku_list]```. – lwashington27 Nov 19 '22 at 00:34
  • 1
    @lwashington27 FYI Programs await on awaitable objects, not functions. An expression like `await f(x)`, where f(x) is a coroutine, actually awaits on the value RETURNED by the function call. A coroutine, when called, returns a coroutine object, which is an awaitable. Coroutines are similar to generators in this sense: calling a generator returns a generator iterator, which yields a sequence of values. A coroutine executes its body when awaited, interacting with a running event loop at each await expression. Its returned value becomes the value of the await expression. – Paul Cornelius Nov 19 '22 at 09:48

1 Answers1

1

When you want to run asynchronous functions from synchronous functions, you have to use the asyncio library to run them. Your last function should look like this.

def products_processor_concurrent(data_parser):
    from asyncio import run
    return run(async_get_products(data_parser))
lwashington27
  • 320
  • 2
  • 14
  • this is great. It bypass the error but now it is showing a new one. I don't want to bother you but it is actually a really new one for me. TypeError: object dict can't be used in 'await' expression I am edditing the question to add the trace – bittrago Nov 18 '22 at 18:26