Менеджер контекста, который обрабатывает исключения
Я пытаюсь понять, как написать диспетчер контекста, который занимается записью некоторых журналов при обработке любых исключений. Проблема, которую я пытаюсь решить, состоит в следующем:
try:
# code that can raise exception here
except Exception as e:
print('failed', e)
print('all good')
Это повторяющийся шаблон, который у меня есть в коде, и я думаю, что с ним лучше всего справиться с помощью диспетчера контекста, например:
with my_ctx_manager(success_msg='all good', failed_msg='failed):
# code that can raise exception here
это выглядит намного лучше, но я не знаю, как написать фактический диспетчер контекста для обработки любых исключений, которые могут возникнуть внутри контекста.
@contextlib.contextmanager
def my_ctx_manager(success_msg, failed_msg):
try:
# if no exception then print(success_msg)
# How do I catch any exception here
except Exception:
print(failed_msg)
# I need the exception to propagate as well
raise
Я предполагаю, что мой вопрос больше типа: как мне убедиться, что диспетчер контекста правильно улавливает, регистрирует и повторно вызывает любое исключение для кода, который обертывает?
2 ответа
Путь @contextmanager
декоратор работает, вы должны написать yield
один раз в вашей функции диспетчера контекста, так что with
блок будет выполняться, пока yield
оператор приостанавливает выполнение вашей функции. Это означает, что еслиwith
блок генерирует исключение, вы можете поймать его, обернув yield
в try
/except
блок:
from contextlib import contextmanager
@contextmanager
def example():
print('entered the context manager')
managed_resource = 'some resource'
try:
yield managed_resource
except Exception as e:
print('caught:', e)
# any cleanup that should only be done on failure
raise
else:
# any cleanup that should only be done on success
print('no exception was thrown')
finally:
# any cleanup that should always be done
print('exited the context manager')
with example() as resource:
print('resource:', resource)
raise ValueError('some error message')
Выход:
entered the context manager
resource: some resource
caught: some error message
exited the context manager
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
ValueError: some error message
Если вы хотите поймать все (а не только Exception
), то вы можете написать голый except:
блокировать и использовать sys.exc_info()
чтобы получить информацию об исключении.
Вместо того, чтобы использовать contextmanager
, который я считаю немного неуклюжим для этой задачи (вам нужно подделать генератор с yield только для регистрации исключения), напишите менеджер контекста самостоятельно. Это просто класс с двумя специальными методами. И с предопределеннымAbstractContextManager
вам нужно реализовать только один из них:
import contextlib
class ExceptionLogger(contextlib.AbstractContextManager):
def __exit__(self, exc_type, exc_value, traceback):
if exc_type:
print("***Logging exception {}***".format((exc_type, exc_value,
traceback)))
with ExceptionLogger():
raise ValueError("foo")