Asyncio: проверка реакции задачи на cancel() с таймаутом
Напишите решение на Python и проверьте его на странице.
Другие упражнения по этой теме: Отмена и таймауты asyncio: задачи на cancel, shield и graceful shutdown.
Аттестация отмены корутин
Инспектор тестовой платформы проверяет, насколько корректно корутины подключаемых плагинов реагируют на отмену выполнения. Для проверки инспектор запускает задачу плагина, дожидается её входа в рабочий цикл через переключение контекста, отправляет запрос task.cancel() и ограничивает время ожидания реакции с помощью asyncio.timeout.
Если задача успевает завершиться до истечения таймаута, инспектор регистрирует её фактический исход: штатный возврат значения, подтверждённое исключение CancelledError или возникшую прикладную ошибку. Однако недобросовестный плагин может подавить отмену и продолжить работу. Если срок ожидания истёк, а задача осталась незавершённой со статусом done() is False, инспектор не пытается её принудительно уничтожить, а передаёт саму работающую задачу вместе с флагом зависания внешнему стенду для перевода цикла событий в режим изоляции.
Реализуйте функцию аттестации корутины, которая определяет тип завершения задачи после сигнала отмены либо фиксирует её зависание за пределами таймаута с сохранением активного объекта задачи.
Что уже дано
В среде выполнения доступна вспомогательная корутина wait_for_task_completion(tasks), которая пассивно ожидает завершения набора задач без отмены или изменения их состояния. Также тестовое окружение предоставляет фикстуры make_attestation_probe, make_attestation_example_probe, observe_attestation и observe_prepared_attestation для запуска сценариев аттестации.
Вам необходимо реализовать асинхронную функцию attest_cancellation.
Что нужно сделать
Реализуйте корутину attest_cancellation(plugin_coro, timeout_seconds).
Функция принимает два аргумента:
plugin_coro— однократный объект корутины подключаемого плагина;timeout_seconds— положительное число секунд ожидания (от 0.01 до 0.1 с).
Порядок выполнения аттестации:
- Оберните корутину
plugin_coroв отдельную нативную задачуasyncio.Task. - Передайте управление циклу событий (например, через
await asyncio.sleep(0)), чтобы задача плагина успела начать выполнение. - Запросите отмену созданной задачи вызовом
task.cancel()(учитывайте, что задача могла завершиться уже на предыдущем шаге). - Ожидайте реакцию задачи на отмену в рамках контекста
asyncio.timeout(timeout_seconds).
Функция должна вернуть словарь ровно с пятью ключами: {"kind", "value", "error", "task", "hung"}:
"returned"— задача штатно завершилась возвратом значения (включаяNone,False, пустые списки или словари):kind="returned",valueсодержит возвращённое значение без изменений,error=None,task=None,hung=False."cancelled"— задача подтвердила отмену черезasyncio.CancelledError(или его подкласс):kind="cancelled",value=None,error=None,task=None,hung=False."error"— задача завершилась прикладным исключением из семействаException(включая прикладныеTimeoutErrorплагина):kind="error",errorсодержит исходный объект исключения плагина,value=None,task=None,hung=False."pending"— время ожиданияasyncio.timeoutистекло, а задача плагина так и не перешла в состояниеdone() is True:kind="pending",taskсодержит сам живой объект задачи плагина,value=None,error=None,hung=True. Не уничтожайте и не отменяйте повторно зависшую задачу.
Прикладное исключение TimeoutError, возникшее внутри самого плагина до истечения внешнего лимита времени, классифицируется как "error", а не как зависание "pending".
Ввод и вывод
Стандартные потоки ввода stdin и вывода stdout в решении не используются: считывать ввод и вызывать print() не нужно. После выполнения кода учащегося проверяющая система считывает из пространства имён модуля функцию верхнего уровня attest_cancellation и вызывает её с проверочными аргументами.
В примере ниже корутина плагина ожидает событие, перехватывает доставленный asyncio.CancelledError и возвращает число 7. Так как задача завершилась штатным возвратом до истечения лимита в 0.04 секунды, итоговый отчёт содержит статус 'returned', само значение 7, пустые поля ошибки и задачи, а флаг зависания равен False.
Пример использования
import asyncio
async def plugin():
try:
await asyncio.Event().wait()
except asyncio.CancelledError:
return 7
report = asyncio.run(attest_cancellation(plugin(), 0.04))
print(report['kind'], report['value'], report['error'], report['task'], report['hung'])
Вывод:
returned 7 None None False
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Связанные задачи
- Asyncio CancelledError: почему except Exception не ловит отмену
- Graceful shutdown Python: завершение текущей работы перед остановкой воркера
- Asyncio timeout: остановка по таймауту и внешней отмене
- Asyncio shield: сохранение начатой работы при повторной отмене
- Удаление ключа из ChainMap в Python: проверка поведения del
- Обратный индекс на Python: группировка уникальных значений через defaultdict
