Python async request call inside loop

I'm trying to develop a Python API with FastAPI, and I have to download a series of images from several URLs, in a foor loop.

The problem is that I have to retrieve the images, and I can't do it as it is an async operation inside a normal for loop.

Here is my code:

@app.post("/v1/img_color")
async def img_color(request: Request):
    body = await request.json()
    images = []
    
    for img in body['images']:
        img_id = img['id']
        img_url = img['url']
        r = requests.get(img_url)
        content = await r.content
        dw_image = Image.open(BytesIO(content))
        images.append(dw_image)
    

    return images

But it gives me the following error:

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

How can I solve this? I've searched about this issue, and found some solutions regarding asyncio but I can't manage to make them work.

Update:

Following the suggestion of deleting the await code from await r.content, I've reached to another error, which says the following:

TypeError('cannot convert dictionary update sequence element #0 to a sequence'), TypeError('vars() argument must have dict attribute')

I don't quite understand this, as I can retrieve perfectly the url from the JSON body of the original POST request...

Traceback

Traceback (most recent call last):
  File "d:\users\kendo\documents\git\empathy\kala_api\env\lib\site-packages\uvicorn\protocols\http\h11_impl.py", line 388, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "d:\users\kendo\documents\git\empathy\kala_api\env\lib\site-packages\uvicorn\middleware\proxy_headers.py", line 45, in __call__
    return await self.app(scope, receive, send)
  File "d:\users\kendo\documents\git\empathy\kala_api\env\lib\site-packages\fastapi\applications.py", line 190, in __call__
    await super().__call__(scope, receive, send)
  File "d:\users\kendo\documents\git\empathy\kala_api\env\lib\site-packages\starlette\applications.py", line 111, in __call__
    await self.middleware_stack(scope, receive, send)
  File "d:\users\kendo\documents\git\empathy\kala_api\env\lib\site-packages\starlette\middleware\errors.py", line 181, in __call__
    raise exc from None
  File "d:\users\kendo\documents\git\empathy\kala_api\env\lib\site-packages\starlette\middleware\errors.py", line 159, in __call__
    await self.app(scope, receive, _send)
  File "d:\users\kendo\documents\git\empathy\kala_api\env\lib\site-packages\starlette\exceptions.py", line 82, in __call__
    raise exc from None
  File "d:\users\kendo\documents\git\empathy\kala_api\env\lib\site-packages\starlette\exceptions.py", line 71, in __call__
    await self.app(scope, receive, sender)
  File "d:\users\kendo\documents\git\empathy\kala_api\env\lib\site-packages\starlette\routing.py", line 566, in __call__
    await route.handle(scope, receive, send)
  File "d:\users\kendo\documents\git\empathy\kala_api\env\lib\site-packages\starlette\routing.py", line 227, in handle
    await self.app(scope, receive, send)
  File "d:\users\kendo\documents\git\empathy\kala_api\env\lib\site-packages\starlette\routing.py", line 41, in app
    response = await func(request)
  File "d:\users\kendo\documents\git\empathy\kala_api\env\lib\site-packages\fastapi\routing.py", line 196, in app
    response_data = await serialize_response(
  File "d:\users\kendo\documents\git\empathy\kala_api\env\lib\site-packages\fastapi\routing.py", line 124, in serialize_response

    return jsonable_encoder(response_content)
  File "d:\users\kendo\documents\git\empathy\kala_api\env\lib\site-packages\fastapi\encoders.py", line 102, in jsonable_encoder
    jsonable_encoder(
  File "d:\users\kendo\documents\git\empathy\kala_api\env\lib\site-packages\fastapi\encoders.py", line 140, in jsonable_encoder
    return jsonable_encoder(
  File "d:\users\kendo\documents\git\empathy\kala_api\env\lib\site-packages\fastapi\encoders.py", line 88, in jsonable_encoder
    encoded_value = jsonable_encoder(
  File "d:\users\kendo\documents\git\empathy\kala_api\env\lib\site-packages\fastapi\encoders.py", line 88, in jsonable_encoder
    encoded_value = jsonable_encoder(
  File "d:\users\kendo\documents\git\empathy\kala_api\env\lib\site-packages\fastapi\encoders.py", line 139, in jsonable_encoder
    raise ValueError(errors)
ValueError: [TypeError('cannot convert dictionary update sequence element #0 to a sequence'), TypeError('vars() argument must have __dict__ attribute')]

Upvotes: 0

Views: 761

Answers (1)

Tomalak
Tomalak

Reputation: 338326

requests.get() is synchronous. There is nothing to await. When the call is done, the content bytes are completely available. Remove the await.

for img in body['images']:
    r = requests.get(img['url'])
    dw_image = Image.open(BytesIO(r.content))
    images.append( (img['id'], dw_image) )

If you're feeling adventurous (or want to learn about dict comprehensions), you can replace your entire loop with one line

@app.post("/v1/img_color")
async def img_color(request: Request):
    body = await request.json()
    return {img['id']: Image.open(BytesIO(requests.get(img['url']).content)) for img in body['images']}

Upvotes: 1

Related Questions