Как манипулировать исключением при выходе из context manager?

Я знаю, что это плохой стиль, чтобы повторно вызвать исключение из контекстного менеджера __exit__() метод. Итак, я хотел бы прикрепить атрибут к экземпляру, который может нести контекстную информацию, которая недоступна, если я позволю исключению просочиться или если я его поймаю. Это позволит избежать его повторного поднятия.

альтернативой привязке атрибута к исключению было бы проглотить исключение, установить некоторое состояние на экземпляре, который удваивается как менеджер контекста в вопросе и позже проверьте это состояние. Проблема в том, что это приведет к Уловке 22, не так ли? Поскольку исключение означает, что выполнение внутри with блок выходящего. Нет никакого способа повторить операцию, кроме как ввести with блок снова, верно? Таким образом, экземпляр, в котором я пытаюсь сохранить контекстную информацию, исчезнет после __exit__() возвращает метод.

короче говоря: как я могу манипулировать фактическим исключением, которое ожидает (если это так, что я предположу как дали на этот вопрос), а в __exit__() способ?

1 ответов


менеджер контекста не уходит только потому, что блок выходит. Вы можете сохранить его двумя способами:

  1. сначала создайте менеджер контекста, назначьте его переменной, затем используйте with С этим объектом:

    cm = ContextManager()
    with cm:
        # ....
    
    state = cm.attribute
    
  2. верните сам менеджер контекста из __enter__ способ использования with ... as ... чтобы привязать это к локальному имени. Это имя не развязано, когда with выходы:

    with ContextManager as cm:
        # ....
    
    state = cm.attribute
    

    здесь ContextManager.__enter__ использует return self.

вы также можете установить дополнительные атрибуты самого исключения; нет необходимости повторно вызывать исключение:

>>> class ContextManager(object):
...     def __enter__(self):
...         return self
...     def __exit__(self, tp, v, tb):
...         if tp is None: return
...         v.extra_attribute = 'foobar'
...         self.other_extra_attribute = 'spam-n-ham'
... 
>>> try:
...     with ContextManager() as cm:
...         raise ValueError('barfoo')
... except ValueError as ex:
...     print vars(ex)
... 
{'extra_attribute': 'foobar'}
>>> vars(cm)
{'other_extra_attribute': 'spam-n-ham'}

здесь исключению был предоставлен дополнительный атрибут, который сохранялся до самого обработчика исключений. В приведенном выше я также показываю, что cm по-прежнему привязан к контексту менеджер.