首页 > 解决方案 > 异步收集异常处理程序不起作用

问题描述

我正在尝试将 exception_handler 与 asyncio.gather 一起使用

我的代码:

import asyncio

def exception_handler(loop, context):
    print('Exception handler called')

async def coro():
    print("coro")
    raise RuntimeError("BOOM!")
    return 1

def main():
    loop = asyncio.get_event_loop()

    loop.set_exception_handler(exception_handler)
    group = asyncio.gather(coro(), coro(), coro())
    results = loop.run_until_complete(group)
    print(results)

main()

当我运行它时,我得到的不是“调用异常处理程序”,而是:

Traceback (most recent call last):
  File "test.py", line 23, in <module>
    main()
  File "test.py", line 20, in main
    results = loop.run_until_complete(group)
  File "/usr/lib64/python3.7/asyncio/base_events.py", line 583, in run_until_complete
    return future.result()
  File "test.py", line 12, in coro
    raise RuntimeError("BOOM!")

标签: pythonexceptionpython-asyncio

解决方案


似乎 Python 文档不清楚asyncio异常处理程序实际上应该处理什么。

引用此错误报告中的 Python 核心开发人员 Andrew Svetlov 的话:

asyncio 异常处理程序应该只捕获未处理的异常,而不是所有引发的异常。

[...] 衍生任务的异常不会隐式传播到父任务。这就是为什么未处理异常并将其传递给已注册的异常处理程序的原因。

该报告给出了以下示例:

async def test():
    raise Exception("Something goes wrong")

async def main():
    #Un-comment either 1 of the following 3 lines
    # await test() # will not call exception_handler
    # await asyncio.gather(test()) # will not call exception_handler
    # asyncio.create_task(test()) # will call exception_handler

    await asyncio.sleep(5)

在您的情况下,您可以处理任何同步代码中的异常:

import asyncio

async def coro():
    print("coro")
    raise RuntimeError("BOOM!")
    return 1

def main():
    loop = asyncio.get_event_loop()

    group = asyncio.gather(coro(), coro(), coro())
    try:
        results = loop.run_until_complete(group)
    except RuntimeError:
        # handle exception
    print(results)

main()

推荐阅读