Flask中TypeError:'async_generator' object is not iterable问题排查
Flask搭建Telegram文件流服务的异步生成器报错问题解决
我尝试在Flask中搭建Telegram文件流服务,核心代码如下:
from flask import Response @app.route("/dl/<hash>", methods=["GET"]) async def download(hash): body = yield_file() return Response( status=206 if range_header else 200, response=body, headers={ "Content-Type": f"{mime_type}", "Content-Range": f"bytes {from_bytes}-{until_bytes}/{file_size}", "Content-Length": str(req_length), "Content-Disposition": f'{disposition}; filename="{file_name}"', "Accept-Ranges": "bytes", }, )
其中yield_file()是一个异步生成器函数,在原项目的aiohttp服务器中运行正常,但迁移到Flask后出现以下报错:
127.0.0.1 - - [03/Aug/2023 22:21:06] "GET /dl/jRtYoKqEu6 HTTP/1.1" 500 - Error on request: Traceback (most recent call last): File "C:\Users\hp\AppData\Roaming\Python\Python310\site-packages\werkzeug\serving.py", line 364, in run_wsgi execute(self.server.app) File "C:\Users\hp\AppData\Roaming\Python\Python310\site-packages\werkzeug\serving.py", line 327, in execute for data in application_iter: File "C:\Users\hp\AppData\Roaming\Python\Python310\site-packages\werkzeug\wsgi.py", line 289, in __next__ return self._next() File "C:\Users\hp\AppData\Roaming\Python\Python310\site-packages\werkzeug\wrappers\response.py", line 32, in _iter_encoded for item in iterable: TypeError: 'async_generator' object is not iterable
问题原因
你的怀疑完全正确:Flask默认基于WSGI协议,Werkzeug(Flask的底层WSGI工具库)在处理响应体时,使用同步for循环遍历迭代对象,但yield_file()是异步生成器,只能通过async for语法遍历,两者不兼容,因此抛出类型错误。
解决方法
方法1:将异步生成器转为同步迭代器
通过异步IO工具把异步生成器包装成同步迭代器,让Werkzeug可以正常遍历:
import asyncio from flask import Response def sync_wrap_async_gen(async_gen): """把异步生成器转为同步迭代器""" loop = asyncio.get_event_loop() async def get_next(): try: return await async_gen.__anext__(), False except StopAsyncIteration: return None, True while True: item, done = loop.run_until_complete(get_next()) if done: break yield item @app.route("/dl/<hash>", methods=["GET"]) async def download(hash): body = sync_wrap_async_gen(yield_file()) # 包装异步生成器 return Response( status=206 if range_header else 200, response=body, headers={ "Content-Type": f"{mime_type}", "Content-Range": f"bytes {from_bytes}-{until_bytes}/{file_size}", "Content-Length": str(req_length), "Content-Disposition": f'{disposition}; filename="{file_name}"', "Accept-Ranges": "bytes", }, )
方法2:改用ASGI模式运行Flask(推荐)
Flask 2.0及以上版本支持ASGI协议,搭配ASGI服务器(如Uvicorn)可以原生处理异步生成器,无需修改核心逻辑:
- 安装依赖:
pip install uvicorn flask
- 修改启动方式(假设你的Flask app文件名为
app.py):
# app.py末尾添加 if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=5000)
- 直接运行
python app.py,此时Flask以ASGI模式运行,Werkzeug会用异步方式处理响应体,兼容异步生成器。
方法3:重写yield_file()为同步生成器
如果不需要异步特性,可以将原异步生成器改写成同步版本,直接使用普通yield语法生成数据,这样就能和Flask的同步WSGI环境兼容。
内容的提问来源于stack exchange,提问作者TechShreyash
相关产品推荐
相关产品推荐

