我有一个大型项目,它依赖于一些第三方库,有时它的执行会被CancelledError中断。
为了演示这个问题,让我们看一个小例子:
import asyncio
async def main():
task = asyncio.create_task(foo())
# Cancel the task in 1 second.
loop = asyncio.get_event_loop()
loop.call_later(1.0, lambda: task.cancel())
await task
async def foo():
await asyncio.sleep(999)
if __name__ == '__main__':
asyncio.run(main())
回溯:
Traceback (most recent call last):
File "/Users/ss/Library/Application Support/JetBrains/PyCharm2021.2/scratches/async.py", line 19, in <module>
asyncio.run(main())
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/runners.py", line 43, in run
return loop.run_until_complete(main)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/base_events.py", line 579, in run_until_complete
return future.result()
concurrent.futures._base.CancelledError
如您所见,没有关于CancelledError的发源地的任何信息。我该怎么找出原因呢?
我想出的一种方法是放置大量的试/除块,这将捕获CancelledError并缩小它的来源。但这很乏味。
发布于 2022-03-04 19:42:40
我通过在项目中的每个异步函数中应用一个装饰器来解决这个问题。当从函数引发CancelledError时,装饰者的工作很简单-记录一条消息。这样,我们将看到哪些函数(更重要的是,按哪个顺序)被取消。
下面是装饰代码:
def log_cancellation(f):
async def wrapper(*args, **kwargs):
try:
return await f(*args, **kwargs)
except asyncio.CancelledError:
print(f"Cancelled {f}")
raise
return wrapper
为了在任何地方添加这个装饰器,我使用regex。查找:(.*)(async def)
.替换为:$1@log_cancellation\n$1$2
。
另外,为了避免在每个文件中导入log_cancellation
,我修改了builtins:builtins.log_cancellation = log_cancellation
发布于 2022-11-17 12:23:03
rich
包帮助我们识别了CancelledError
的原因,而无需进行太多的代码更改。
from rich.console import Console
console = Console()
if __name__ == "__main__":
try:
asyncio.run(main()) # replace main() with your entrypoint
except BaseException as e:
console.print_exception(show_locals=True)
https://stackoverflow.com/questions/71324885
复制相似问题