Это пример отмены задачи:
import asyncio
async def some_func():
await asyncio.sleep(2)
print('Haha! Task keeps running!')
await asyncio.sleep(2)
async def cancel(task):
await asyncio.sleep(1)
task.cancel()
async def main():
func_task = asyncio.ensure_future(some_func())
cancel_task = asyncio.ensure_future(cancel(func_task))
try:
await func_task
except asyncio.CancelledError:
print('Task cancelled as expected')
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
# Task cancelled as expected
# [Finished in 1.2s]
Работает нормально, задача отменена. Если CancelledError
попал в some_func
, задача не будет отменена:
async def some_func():
try:
await asyncio.sleep(2)
except:
pass
print('Haha! Task keeps running!')
await asyncio.sleep(2)
# Haha! Task keeps running!
# [Finished in 3.2s]
Легко забыть, что я не должен подавлять исключения где-либо внутри асинхронного кода (или some_func
может быть, например, сторонним кодом), но задачу следует отменить. Как я могу это сделать? Или игнорируется CancelledError
означает, что задача не может быть отменена вообще?
2 ответа
Вы не можете отменить задачу, которая подавляет CancelledError
. Это похоже на невозможность закрыть генератор, который игнорирует GeneratorExit
.
Это преднамеренное поведение. Задаче может потребоваться дополнительная работа (например, очистка ресурсов) при отмене, поэтому перехват CancelledError
может быть хорошей идеей, но подавление обычно является признаком ошибки программирования.
Python обычно позволяет вам стрелять собственными ногами, если у вас есть бескомпромиссное намерение сделать это.
Перехват всех исключений запрещает даже закрытие процесса python нажатием <Ctrl+C>
, потому что он внутренне переведен в KeyboardInterrupt
.
Настоящая проблема здесь заключается в использовании общего предложения «за исключением», которое либо почти никогда не бывает хорошей идеей, либо, по мнению некоторых, абсолютно никогда не бывает хорошей идеей.
Вместо того, чтобы использовать «except:» отдельно, всегда указывайте исключение или базовый класс исключения, который вы хотите перехватить. Обратите внимание, что начиная с Python 3.8 CancelledError является подклассом BaseException, а не Exception, тогда как все исключения, которые вы обычно хотите перехватывать с помощью «except:», являются подклассами Exception (который сам является дочерним классом BaseException). Вот документы по иерархии исключений, показывающие эту взаимосвязь.
Так что лучше:
try:
... (your code here)
except Exception:
pass
Это будет перехватывать и молча игнорировать все обычные исключения (что по-прежнему нежелательно в большинстве реальных кодов, но определенно допустимо для некоторых), в то же время позволяя другим пройти. Обратите внимание, что CancelledError, SystemExit, KeyboardInterrupt и несколько других все равно пройдут. Если вам не нравится «шум», производимый теми на выходе, то вам следует ловить их специально (скорее всего) на более высоком уровне.
Если вы действительно хотите проглотить буквально все, кроме CancelledError, вы должны сделать это вместо этого:
try:
... (your code here)
except asyncio.CancelledError:
raise # avoid swallowing this one exception
except BaseException:
pass # silently swallow everything else: usually not a good idea
Похожие вопросы
Связанные вопросы
Новые вопросы
python
Python — это мультипарадигмальный многоцелевой язык программирования с динамической типизацией. Он предназначен для быстрого изучения, понимания и использования, а также обеспечивает чистый и унифицированный синтаксис. Обратите внимание, что Python 2 официально не поддерживается с 01.01.2020. Если у вас есть вопросы о версии Python, добавьте тег [python-2.7] или [python-3.x]. При использовании варианта Python (например, Jython, PyPy) или библиотеки (например, Pandas, NumPy) укажите это в тегах.