Контекст
Во-первых, давайте рассмотрим более широкий контекст:
caller() --> your_coro() --> callee()
Вы управляете своей сопрограммой, но не контролируете вызывающих и только вчастичный контроль вызываемых абонентов.
По умолчанию отмена эффективно "распространяется" как вверх, так и вниз по стеку:
(1)
caller1() ------------------+ (2)
+--> callee()
caller2() --> your_coro() --+
(4) (3)
На этой диаграмме семантически и очень свободно , если caller1()
активно отменяется, то callee()
отменяется, и тогда ваша сопрограмма отменяется, а затем caller2()
отменяется.Примерно то же самое происходит, если caller2()
активно отменяется.
(callee()
является общим, и, следовательно, не простой сопрограммой, скорее Task
или Future
)
Что альтернативное поведение возможно, вы захотите?
Щит
Если вы хотите, чтобы callee()
продолжалось, даже если caller2()
отменено, shield
это:
callee_f = asyncio.ensure_future(callee())
async def your_coro():
# I might die, but I won't take callee down with me
await asyncio.shield(callee_f)
Обратный щит
Если вы позволите callee()
умереть, но хотите, чтобы ваша сопрограмма продолжалась, преобразуйте исключение :
async def reverse_shield(awaitable):
try:
return await awaitable
except asyncio.CancelledError:
raise Exception("custom")
async def your_coro():
await reverse_shield(callee_f)
# handle custom exception
Защитите себя
Этот вопрос сомнителен - обычно вы должны разрешить своему вызывающему абоненту отменять сопрограмму.
Примечательным исключением является случай, когда ваш вызывающий абонент является фреймворком, и он не настраивается.
def your_coro():
async def inner():
...
return asyncio.shield(inner())