Менеджер контекста, который обрабатывает исключения - PullRequest
0 голосов
/ 24 февраля 2020

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

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 ]

2 голосов
/ 24 февраля 2020

Вместо использования contextmanager, который я нахожу немного неуклюжим для этой задачи (вам нужно подделать генератор с выходом только для регистрации исключения), напишите менеджер контекста самостоятельно. Это просто класс с двумя специальными методами. А с предопределенным 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")
2 голосов
/ 24 февраля 2020

Как работает декоратор @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() для получения информации об исключении.

...