一种pythonic的方式来改变如何从调用cancel()的wery nory()取消ASYNCIO任务



如何从取消任务取消的任务取消行为?

我梦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 :(
   [...]

我正在寻找一种更多的pythonic方法。

您的解决方案以与您的例外相关的数据来装饰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装饰Coroutine允许foo_done中的代码在处理CancelledError时访问e.when。缺点是e.when将在任务中可用不可用 - 您仍然必须使用current_task()。由于这种不一致,我建议坚持从任务对象阅读。

几个相关建议:

  • 将取消代码放在存储您传递的对象的实用程序函数中,然后调用task.cancel()。这种薄的封装层应从当前代码中删除"笨拙"的感觉。

  • 使用前缀属性名称 - 简短且通用的属性名称(例如_when)可能会在将来的版本中引起冲突。(我知道这只是一个例子,但是未修复的名称总是有冲突的危险。)

  • 用一个对象对任务进行装饰,将实际数据放在其属性中。它使检索变得更简单,更清洁,并且可以选择在存储的对象上实现方法。

最新更新