Python 创建后存储asyncio任务时,任务中的异常将被禁用
我在一个项目中使用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
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