Отмена асинхронного диспетчера контекста - PullRequest
1 голос
/ 06 апреля 2019

Достаточно просто получить дескриптор задачи для отмены:

task = loop.create_task(coro_fn())

# later
task.cancel()

Можно ли сделать то же самое для асинхронных контекстных менеджеров?

async with foo() as bar:
   # is it possible to cancel before getting here,
   # while waiting to enter the context manager?
   await bar.baz()

# later
# How do I get a handle to the context manager for cancellation?

Есть ли способ?сделать это?Или код менеджера контекста должен выполняться в своей собственной задаче?

1 Ответ

3 голосов
/ 06 апреля 2019

Как получить дескриптор диспетчера контекста для отмены?

Вы, по крайней мере, не напрямую, - менеджер контекста - это просто удобная упаковка для методов получения и освобождения ресурса. async with foo() as bar: ... от вашего вопроса desugars к чему-то вроде:

_cm = foo()
bar = await _cm.__aenter__()
try:
    await bar.baz()
finally:
    await _cm.__aexit__(*sys.exc_info())

И __aenter__, и __aexit__ являются обычными ожидаемыми, чье выполнение может быть отменено, точно так же, как выполнение await bar.baz() внутри тела async with.

Таким образом, если под "отменой диспетчера контекста" вы подразумеваете отмену операции, в которой в данный момент заблокирован блок async with, вы можете сделать это так же, как и для любой другой отмены. Например, вы можете извлечь async with в сопрограмму, которая выполняет свою собственную задачу, и cancel(). Обратите внимание, что вам все равно нужно будет получить задание, чтобы даже перейти к разделу «позже» без закрытия асинхронного диспетчера контекста.

Вот пример:

async def run_foo():
    async with foo() as bar:
       # is it possible to cancel before getting here,
       # while waiting to enter the context manager?
       await bar.baz()

task = asyncio.create_task(run_foo())
# "task" is the handle you can await or cancel at your leisure
...