【问题标题】:Python async request call inside loop循环内的Python异步请求调用
【发布时间】:2020-12-06 21:11:19
【问题描述】:

我正在尝试使用 FastAPI 开发 Python API,我必须在一个循环中从多个 URL 下载一系列图像。

问题是我必须检索图像,而我不能这样做,因为它是正常 for 循环中的异步操作。

这是我的代码:

@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

但它给了我以下错误:

TypeError: 对象字节不能用于 'await' 表达式

我该如何解决这个问题?我已经搜索过这个问题,并找到了一些关于 asyncio 的解决方案,但我无法让它们工作。

更新:

按照从await r.content 中删除await 代码的建议,我遇到了另一个错误,它显示以下内容:

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

我不太明白这一点,因为我可以从原始 POST 请求的 JSON 正文中完美检索 url...

追溯

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')]

【问题讨论】:

  • requests.get() 是同步的。 await 没有任何内容。调用完成后,内容字节完全可用。删除await
  • 这能回答你的问题吗? How could I use requests in asyncio?
  • @Tomalak 我删除了await 代码,但现在它给了我一个新错误(请参阅更新),知道吗?提前致谢!
  • 由于我不知道该错误发生在哪一行,所以我无法帮助您。
  • 我猜:你做的是return images,这是Image 对象的列表。我想您的框架已配置为尝试将其转换为 JSON,并且 似乎 尝试将您的 [Image1, Image2] 转换为对象 {"key1": "value1", "key2": "value2"}。那是行不通的,因为那里没有任何钥匙。您可以尝试return enumerate(images) 以查看错误是否消失。你需要弄清楚你想要从你的端点返回什么输出,以及你需要从你的处理函数返回什么来获取它。

标签: python for-loop asynchronous async-await


【解决方案1】:

requests.get() 是同步的。没有什么可以等待的。调用完成后,内容字节完全可用。删除await

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

如果您喜欢冒险(或想了解 dict 理解),您可以用一行替换整个循环

@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']}

【讨论】:

    猜你喜欢
    • 2019-07-21
    • 2017-11-22
    • 1970-01-01
    • 1970-01-01
    • 2018-10-17
    • 1970-01-01
    • 2012-09-10
    • 2019-03-24
    相关资源
    最近更新 更多