如何从取消任务的位置更改取消任务的行为?
我梦dream以求的东西
task = ensure_future(foo())
def foo_done(task)
try:
return task.get_result()
except CancelError as e:
when, why = e.args
if when == "now"
# do something...
elif when == "asap":
# do something else...
else:
# do default
print(f"task cancelled because {why}")
task.add_done_callback(foo_done)
[...]
task.cancel("now", "This is an order!")
我可以在调用task.cancel()
之前将对象附加到任务上,然后再检查它。
task = ensure_future(foo())
def foo_done(task)
try:
return task.get_result()
except CancelError as e:
when = getattr(task, "_when", "")
why = getattr(task, "_why", "")
if when == "now"
# do something...
elif when == "asap":
# do something else...
else:
# do default
print(f"task cancelled because {why}")
task.add_done_callback(foo_done)
[...]
task._when = "now"
task._why = "This is an order!"
task.cancel()
但是在某些情况下,例如当我想捕获正在处理的任务中的CancelError
时,它显得笨拙:
async def foo():
# some stuff
try:
# some other stuff
except CancellError as e:
# here I have easily access to the error, but not the task :(
[...]
我正在寻找一种更Python化的方法。
答案 0 :(得分:2)
您的解决方案用与您的异常相关的数据装饰Task
实际上是一个很好的解决方案。在任务内,您可以访问正在使用asyncio.Task.current_task()
处理的任务。
您还可以使用以下装饰器(未经测试)实现您梦dream以求的语法:
def propagate_when(fn):
async def wrapped(*args, **kwds):
try:
return await fn(*args, **kwds)
except CancelledError as e:
e.when = getattr(asyncio.Task.current_task(), '_when', None)
raise
return wrapped
使用@propagate_when
装饰协程可以在处理foo_done
时访问e.when
中的代码。缺点是CancelledError
在任务中将不可用-在此您仍然必须使用e.when
。由于存在这种不一致,我建议坚持从任务对象读取。
几个相关建议:
将取消代码放入存储您传递的对象的实用程序函数中,然后调用current_task()
。这种封装的薄层应该消除当前代码中的“笨拙”感觉。
使用带前缀的属性名称-简短的通用名称(如task.cancel()
会在将来的版本中引起冲突。 (我知道这只是一个例子,但未加前缀的名称始终有发生冲突的危险。)
用单个对象装饰任务,将实际数据放入其属性。它使检索变得更简单,更干净,并且使您可以选择在存储的对象上实现方法。