Сообщить об ошибке.

Функция shield() модуля asyncio в Python.

Функция asyncio.shield() защищает awaitable объекты от отмены.

Синтаксис:

import asyncio

await asyncio.shield(aw, *, loop=None)

Параметры:

  • aw - объект задачи,
  • loop=None - параметр цикла (устарел и будет удален в Python 3.10)

Возвращаемое значение:

  • нет.

Описание:

Функция shield() модуля asyncio защищает объект задачи от отмены методом Task.cancel().

Если аргумент aw является сопрограммой, то она автоматически назначается как задача.

Выражение:

res = await shield(something())

эквивалентно:

res = await something()

за исключением того, что если содержащаяся в выражении сопрограмма отменяется через res.cansel(), то задача, выполняемая в функции something() не отменяется. С точки зрения something() отмены не произошло. Хотя вызывающий ее объект отменен, выражение await по-прежнему вызовет исключение asyncio.CancelledError.

Концептуально, функция похожа на пуленепробиваемый жилет, который поглощает пулю, защищая владельца, но сам разрушается. Так же и asyncio.shield() поглощает отмену и при запросе результата сообщает о себе как об отмененной, вызывая исключение asyncio.CancelledError, но позволяет защищенной задаче продолжать выполнение.

Такое поведение функции гарантирует, что отмена будет "успешной", то есть отменяющая сторона не сможет сказать, что отмену фактически обошли. Это сделано намеренно и в целом делает механизм отмены более последовательным.

Если функция something() отменяется другими способами, например изнутри себя, то такое поведение также отменит asyncio.shield().

Если необходимо полностью игнорировать отмену, что не рекомендуется, то функцию asyncio.shield() следует обернуть в try/except, как показано ниже:

try:
    res = await shield(something())
except CancelledError:
    res = None

С версии Python 3.8 параметр цикла loop устарел и будет удален в Python 3.10

Пример защиты задачи от отмены выполнения.

import asyncio

async def coro():
    print('Старт задачи...')
    await asyncio.sleep(2)
    print('закончили спать')

async def cancel_it(some_task):
    await asyncio.sleep(0.5)
    some_task.cancel()
    print('аннулирование произведено')

async def main():
    real_task = asyncio.create_task(coro())
    shield = asyncio.shield(real_task)
    # отменяем shield в фоновом режиме, пока ждем
    asyncio.create_task(cancel_it(shield))
    await real_task

    assert not real_task.cancelled()
    assert shield.cancelled()

if __name__ == '__main__':
    asyncio.run(main())

# Старт задачи...
# аннулирование произведено
# закончили спать