我希望asyncio.gather
立即引发任何异常,除了某些特定的异常类,应该在结果列表中返回。现在,我只是稍微修改了CPython中asyncio.gather
的规范实现并使用它,但我想知道是否有更规范的方法来实现它。
答案 0 :(得分:2)
您可以使用功能更强大的asyncio.wait
原语及其return_when=asyncio.FIRST_EXCEPTION
选项来实现此类语义:
async def xgather(*coros, allowed_exc):
results = {}
pending = futures = list(map(asyncio.ensure_future, coros))
while pending:
done, pending = await asyncio.wait(
pending, return_when=asyncio.FIRST_EXCEPTION)
for fut in done:
try:
results[fut] = fut.result()
except allowed_exc as e:
results[fut] = e
return [results[fut] for fut in futures]
想法是调用wait
,直到所有期货都完成或观察到异常。该异常依次存储或传播,具体取决于它是否与allowed_exc
匹配。如果已成功收集所有结果和允许的例外,则会按正确的顺序返回,与asyncio.gather
一样。
修改asyncio.gather
实现的方法可能很容易在较新的Python版本上失败,因为代码访问Future
个对象的私有属性。另外,像uvloop
这样的替代事件循环可以提高gather
和wait
的效率,这会根据公共API自动使xgather
受益。
测试代码:
import asyncio
async def fail():
1/0
async def main():
print(await xgather(asyncio.sleep(1), fail(), allowed_exc=OSError))
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
运行时,代码立即引发,预期ZeroDivisionError
与允许的OSError
异常不匹配。将OSError
更改为ZeroDivisionError
会导致代码暂停1秒钟并输出[None, ZeroDivisionError('division by zero',)]
。