Перехват исключений в __enter__ в вызывающем коде на Python

Есть ли способ перехватывать исключения в методе __enter__ контекстного менеджера, не заключая весь блок with в try?

class TstContx(object):
    def __enter__(self):
        raise Exception("I'd like to catch this exception")
    def __exit__(self, e_typ, e_val, trcbak):
        pass


with TstContx():
    raise Exception("I don't want to catch this exception")
    pass

Я знаю, что могу перехватить исключение внутри самого __enter__(), но могу ли я получить доступ к этой ошибке из функции, содержащей оператор with?

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

...видимо мотивация должна быть яснее. Оператор with настраивает регистрацию для полностью автоматизированного процесса. Если программа дает сбой до того, как будет настроено ведение журнала, я не могу полагаться на ведение журнала, чтобы уведомить меня, поэтому я должен сделать что-то особенное. И я бы предпочел добиться эффекта без добавления дополнительных отступов, например:

try:
    with TstContx():
        try:
            print "Do something"
        except Exception:
            print "Here's where I would handle exception generated within the body of the with statement"
except Exception:
    print "Here's where I'd handle an exception that occurs in __enter__ (and I suppose also __exit__)"

Еще одним недостатком использования двух блоков try является то, что код, обрабатывающий исключение в __enter__, идет после кода, обрабатывающего исключение в последующем теле блока with.


person kuzzooroo    schedule 22.07.2014    source источник


Ответы (2)


Вы можете поймать исключение, используя try/except внутри __enter__, а затем сохранить экземпляр исключения как переменную экземпляра класса TstContx, что позволит вам получить к нему доступ внутри блока with:

class TstContx(object):
    def __enter__(self):
        self.exc = None
        try:
            raise Exception("I'd like to catch this exception")
        except Exception as e:
            self.exc = e 
        return self

    def __exit__(self, e_typ, e_val, trcbak):
        pass    

with TstContx() as tst:
    if tst.exc:
        print("We caught an exception: '%s'" % tst.exc)
    raise Exception("I don't want to catch this exception")

Выход:

We caught an exception: 'I'd like to catch this exception'.
Traceback (most recent call last):
  File "./torn.py", line 20, in <module>
    raise Exception("I don't want to catch this exception")
Exception: I don't want to catch this exception

Не уверен, почему вы хотите это сделать, хотя ....

person dano    schedule 22.07.2014
comment
Связанным вариантом может быть return self, exception или (в @contextmanager) использование yield whatever, exception, где exception равно None, если не возникло исключения. - person kuzzooroo; 28.07.2014

Вы можете использовать contextlib.ExitStack, как описано в этот пример документа, чтобы отдельно проверить наличие __enter__ ошибок :

from contextlib import ExitStack

stack = ExitStack()
try:
    stack.enter_context(TstContx())
except Exception:  # `__enter__` produced an exception.
    pass
else:
    with stack:
        ...  # Here goes the body of the `with`.
person a_guest    schedule 04.11.2020