引发 asyncio.CancelledError 后无法使用 sync_to_async 装饰器

Cannot use sync_to_async decorator after raising asyncio.CancelledError

import asyncio
from asgiref.sync import sync_to_async


@sync_to_async
def sync_func():
    print("sync_func() was CALLED (good!)")


async def async_func():
    print("async_func() was CALLED (good!)")


async def test(excep_type: type = asyncio.CancelledError):
    print(f"\nException Type: {excep_type}")
    try:
        raise excep_type
    except:
        await async_func()
        try:
            await sync_func()
        except asyncio.CancelledError:
            print("sync_func() was CANCELLED (bad!)")

asyncio.run(test(excep_type=ValueError))
asyncio.run(test())

你好。我有一个简单的例子

  1. test 是一个异步函数,它除了引发提供的错误类型外什么都不做,然后在内部等待 async_funcsync_func
  2. sync_func 是一个与 asgiref 包的 sync_to_async 异步的同步函数。

运行 这给出了以下输出:

Exception Type: <class 'ValueError'>
async_func() was CALLED (good!)
sync_func() was CALLED (good!)

Exception Type: <class 'asyncio.exceptions.CancelledError'>
async_func() was CALLED (good!)
sync_func() was CANCELLED (bad!)

在第一种情况下,引发了 ValueError 并且在异常子句中,我可以像往常一样等待 async_funcsync_func

但是在第二种情况下,我的行为出乎我的意料。我应该能够像往常一样等待 sync_func,但它会引发 asyncio.CancelledError,如打印输出所示。

实际上,除了更改最初由异常子句引发和捕获的错误类型外,我没有更改两次调用之间的任何内容。然而,引发的错误类型似乎产生了影响。

这意味着现实世界,我不能在任何 clean-up activity of cancelled tasks

中使用 sync_to_async 包装的异步函数

这似乎是由 sync_to_async 装饰器引起的,因为 async_func 运行良好。

我做错了什么?

这是 asgiref 3.3.1 中引入的 sync_to_async 中的错误。

https://github.com/django/asgiref/issues/247

这可以使用内置的 asyncio 功能来解决,或者,如果这不可能,则将失败的协程包装在 create_task 中。