asyncio:loop.run_until_complete(loop.create_task(f))打印出“永远不会检索到任务异常”,即使它已经被传播了

时间:2015-12-23 18:40:32

标签: python python-3.x exception coroutine python-asyncio

由于某种原因,该程序会打印以下警告:

Task exception was never retrieved
future: <Task finished coro=<coro() done, defined at /usr/lib64/python3.4/asyncio/coroutines.py:139> exception=SystemExit(2,)>

即使明确检索并传播了异常,当caught SystemExit!打印到终端时,进程状态代码变为2。

Python 2和trollius也是如此。

我错过了什么吗?

#!/usr/bin/env python3

import asyncio

@asyncio.coroutine
def comain():
    raise SystemExit(2)

def main():
    loop = asyncio.get_event_loop()
    task = loop.create_task(comain())
    try:
        loop.run_until_complete(task)
    except SystemExit:
        print("caught SystemExit!")
        raise
    finally:
        loop.close()

if __name__ == "__main__":
    main()

2 个答案:

答案 0 :(得分:7)

SystemExit似乎是一个特例。例如,如果您举起并抓住Exception,则不会看到任何错误。 解决这个问题的方法似乎是使用Task.exception()手动检索异常:

import asyncio

@asyncio.coroutine
def comain():
    raise SystemExit(2)

def main():
    loop = asyncio.get_event_loop()
    task = loop.create_task(comain())
    try:
        loop.run_until_complete(task)
    except SystemExit:
        print("caught SystemExit!")
        task.exception()
        raise
    finally:
        loop.close()

if __name__ == "__main__":
    main()

修改

实际上,所有BaseException子类都会以这种方式运行。

答案 1 :(得分:0)

我认为这是因为SystemExitKeyboardInterrupt是不同的情况。例如,正常Exception不会导致相同的问题。

您可以将comain协程链接到另一个协程中以消耗这样的异常:

#!/usr/bin/env python3

import asyncio

@asyncio.coroutine
def comain():
    raise SystemExit(2)

@asyncio.coroutine
def another():
    try:
        yield from comain()
    except SystemExit:
        print ("consumed")

def main():
    loop = asyncio.get_event_loop()
    task = loop.create_task(another())
    try:
        loop.run_until_complete(task)
    except SystemExit:
        print("caught SystemExit!")
        raise
    finally:
        loop.close()

if __name__ == "__main__":
    main()

此处 - 我正在调用another()而不是comain()而在another()内我正在处理来自comain()的异常。