一种Pythonic方法,用于更改如何从调用cancel()的位置取消异步IO任务

一种Pythonic方法,用于更改如何从调用cancel()的位置取消异步IO任务,python,python-asyncio,Python,Python Asyncio,如何从取消任务的位置更改任务取消的行为 我的梦想是: task=确保未来(foo()) def foo_完成(任务) 尝试: 返回任务。获取_结果() 除取消错误外,其他错误为e: 何时,为什么=e.args 如果时间==“现在” #做点什么。。。 elif when==“尽快”: #做点别的。。。 其他: #不违约 打印(f“由于{为什么}而取消任务”) task.add_done_回调(foo_done) [...] 任务。取消(“现在”,“这是订单!”) 我可以在调用task.cance

如何从取消任务的位置更改任务取消的行为

我的梦想是:

task=确保未来(foo())
def foo_完成(任务)
尝试:
返回任务。获取_结果()
除取消错误外,其他错误为e:
何时,为什么=e.args
如果时间==“现在”
#做点什么。。。
elif when==“尽快”:
#做点别的。。。
其他:
#不违约
打印(f“由于{为什么}而取消任务”)
task.add_done_回调(foo_done)
[...]
任务。取消(“现在”,“这是订单!”)
我可以在调用
task.cancel()
之前将一个对象附加到任务,然后再检查它

task=确保未来(foo())
def foo_完成(任务)
尝试:
返回任务。获取_结果()
除取消错误外,其他错误为e:
when=getattr(任务“\u when”,“”)
why=getattr(任务“\u why”,”)
如果时间==“现在”
#做点什么。。。
elif when==“尽快”:
#做点别的。。。
其他:
#不违约
打印(f“由于{为什么}而取消任务”)
task.add_done_回调(foo_done)
[...]
任务。_when=“现在”
任务。_why=“这是订单!”
task.cancel()
但在某些情况下,当我想要捕获正在处理的任务中的
CancelError
时,它看起来很笨拙,例如:

async def foo():
#一些东西
尝试:
#一些其他的东西
除取消错误外,如e:
#在这里,我可以轻松访问错误,但无法访问任务:(
[...]

我正在寻找一种更具python风格的方法来完成它。

您用与异常相关的数据来修饰
任务的解决方案实际上是一个很好的解决方案。在任务中,您可以使用
asyncio.Task.current_Task()
访问正在处理的任务

您还可以使用以下装饰器(未经测试)实现您梦想的语法:

@propagate\u when
装饰协同程序允许
foo\u done
中的代码在处理
canceledError
时访问
e.when
。缺点是
e.when
在任务中不可用-您仍然必须使用
current\u task()
。由于这种不一致性,我建议坚持从任务对象读取

若干相关建议:

  • 将取消代码放入一个实用程序函数中,该函数存储传递给它的对象,然后调用
    task.cancel()
    。这种薄薄的封装层应该可以消除当前代码中的“笨拙”感觉

  • 使用带前缀的属性名称-短而通用的名称,如
    \u,当
    在将来的版本中可能会导致冲突。(我知道这只是一个示例,但不固定的名称总是有冲突的危险。)

  • 用单个对象装饰任务,将实际数据放入其属性中。它使检索更简单、更清晰,并且为您提供了在存储对象上实现方法的选项


考虑到
foo_done
是一个同步函数,“现在”和“尽快”有何不同。我意识到“现在”和“尽快”是我想要的糟糕的例子。我的意思是给任务提供取消的原因,并根据它执行一些代码:如果reason=A做这个,如果reason=B,做那个,等等。谢谢!我要找的确实是
asyncio.task.current_task()
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