Python 创建后存储asyncio任务时,任务中的异常将被禁用

Python 创建后存储asyncio任务时,任务中的异常将被禁用,python,python-asyncio,Python,Python Asyncio,我在一个项目中使用asyncio时,遇到了这种奇怪的行为 import asyncio def schedule_something(): global f tsk = asyncio.async(do_something()) f = tsk #If this line is commented out, exceptions can be heard. @asyncio.coroutine def do_something(): raise Excepti

我在一个项目中使用asyncio时,遇到了这种奇怪的行为

import asyncio

def schedule_something():
    global f
    tsk = asyncio.async(do_something())
    f = tsk #If this line is commented out, exceptions can be heard.

@asyncio.coroutine
def do_something():
    raise Exception()

loop = asyncio.get_event_loop()
loop.call_soon(schedule_something)
loop.run_forever()
loop.close()
出于某种原因,在调用
asyncio.async()
时存储生成的任务可以阻止异常执行任何操作


有人能解释一下这种情况吗?我需要有一种方法来捕获当前项目中的异常。

这是因为只有在
任务
被销毁而从未检索其结果时才会引发异常。将
任务
分配给全局变量时,它将始终具有活动引用,因此永远不会被销毁。asyncio/futures.py中有一个docstring详细介绍了以下内容:

class _TracebackLogger:
    """Helper to log a traceback upon destruction if not cleared.

    This solves a nasty problem with Futures and Tasks that have an
    exception set: if nobody asks for the exception, the exception is
    never logged.  This violates the Zen of Python: 'Errors should
    never pass silently.  Unless explicitly silenced.'

    However, we don't want to log the exception as soon as
    set_exception() is called: if the calling code is written
    properly, it will get the exception and handle it properly.  But
    we *do* want to log it if result() or exception() was never called
    -- otherwise developers waste a lot of time wondering why their
    buggy code fails silently.

    An earlier attempt added a __del__() method to the Future class
    itself, but this backfired because the presence of __del__()
    prevents garbage collection from breaking cycles.  A way out of
    this catch-22 is to avoid having a __del__() method on the Future
    class itself, but instead to have a reference to a helper object
    with a __del__() method that logs the traceback, where we ensure
    that the helper object doesn't participate in cycles, and only the
    Future has a reference to it.

    The helper object is added when set_exception() is called.  When
    the Future is collected, and the helper is present, the helper
    object is also collected, and its __del__() method will log the
    traceback.  When the Future's result() or exception() method is
    called (and a helper object is present), it removes the the helper
    object, after calling its clear() method to prevent it from
    logging.
如果要查看/处理异常,只需使用来处理任务的结果,并在出现异常时执行任何必要的操作:

import asyncio

def handle_result(fut):
    if fut.exception():
        fut.result()  # This will raise the exception.

def schedule_something():
    global f
    tsk = asyncio.async(do_something())
    tsk.add_done_callback(handle_result)
    f = tsk

@asyncio.coroutine
def do_something():
    raise Exception()

loop = asyncio.get_event_loop()
loop.call_soon(schedule_something)
loop.run_forever()
loop.close()

谢谢,达诺。这里有一个替代
asyncio的插件。创建一个自动执行此操作的任务-

def创建_任务(coro):
task=asyncio.create_任务(coro)
返回任务包装器(任务)
类任务包装器:
定义初始化(自我,任务):
self.task=任务
task.add_done_回调(self.on_task_done)
def _ugetattr _;(self,name):
返回getattr(self.task,name)
定义等待(自我):
self.task.remove\u done\u回调(self.on\u task\u done)
返回self.task.\uu等待
任务完成时的定义(self,fut:asyncio.Future):
如果未来已取消()或未完成():
返回
未来结果()
定义(自我):
返回f“TaskWrapper”

给定示例的更新版本-

async def do_something():
引发异常()
异步def schedule_something():
全局f
tsk=创建任务(做某事())
f=tsk#如果这一行被注释掉,可以听到异常。
asyncio.run(schedule\u something())
$python test.py
回调TaskWrapper.on_task_done()中出现异常
处理:
回溯(最近一次呼叫最后一次):
文件“/Users/dev/.pyenv/versions/3.8.1/lib/python3.8/asyncio/events.py”,第81行,在运行时
self.\u context.run(self.\u回调,*self.\u参数)
文件“/Users/dev/Projects/dara/server/bot/async_util.py”,第21行,on_task_done
未来结果()
文件“/Users/dev/Projects/dara/server/test.py”,第7行,在do\u something中
引发异常()
例外情况

谢谢,这实际上使我的项目更容易,为特定任务指定异常处理程序,而不是为事件循环中的任何异常指定异常处理程序。这应该是默认行为。
$ python test.py
Exception in callback TaskWrapper.on_task_done(<Task finishe...n=Exception()>)
handle: <Handle TaskWrapper.on_task_done(<Task finishe...n=Exception()>)>
Traceback (most recent call last):
  File "/Users/dev/.pyenv/versions/3.8.1/lib/python3.8/asyncio/events.py", line 81, in _run
    self._context.run(self._callback, *self._args)
  File "/Users/dev/Projects/dara/server/bot/async_util.py", line 21, in on_task_done
    fut.result()
  File "/Users/dev/Projects/dara/server/test.py", line 7, in do_something
    raise Exception()
Exception