Менеджер контекста, который обрабатывает исключения

Я пытаюсь понять, как написать диспетчер контекста, который занимается записью некоторых журналов при обработке любых исключений. Проблема, которую я пытаюсь решить, состоит в следующем:

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")
Другие вопросы по тегам