Контекстный менеджер Python не очищается

Когда я вызываю какие-либо исключения внутри менеджера контекста, код очистки не запускается. Например:

from contextlib import contextmanager

try:
    raise BaseException()
except BaseException:
    print "bye from except"


@contextmanager
def say_goodbye():
    yield
    print "bye from context manager"

with say_goodbye():
    raise BaseException()

Выведет:

bye from except
Traceback (most recent call last):
  File "", line 15, in 
BaseException

Обратите внимание, что оператор try/except правильно перехватывает исключение, а оператор with — нет. Есть ли что-то, чего я не понимаю в том, как должны работать операторы with?

Вы можете увидеть код в скрипке здесь: http://pythonfiddle.com/context-manager-failing


К вашему сведению, я использую python 2.7 на OSX Mavericks. Хотя мне удавалось размножаться во многих средах, поэтому я сомневаюсь, что это имеет к этому какое-то отношение.


person lemiant    schedule 21.11.2014    source источник
comment
Это полностью объясняется в документах contextmanager. Весь последний абзац точно описывает, как это работает, рассказывает, как использовать try/except` или try/finally и т. д.   -  person abarnert    schedule 22.11.2014


Ответы (1)


Вам нужно будет добавить обработку исключений самостоятельно:

@contextmanager
def say_goodbye():
    try:
        yield
    finally:
        print "bye from context manager"

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

@contextmanager
def say_goodbye():
    try:
        yield
    except Exception as exc:
        print(exc)
    finally:
        print "bye from context manager"

Как сказано в документации:

Если в блоке возникает необработанное исключение, оно повторно вызывается внутри генератора в точке, где произошел выход. Таким образом, вы можете использовать оператор try...except...finally, чтобы перехватить ошибку (если она есть) или обеспечить выполнение некоторой очистки.

Также обратите внимание, что BaseException не является Exception:

>>> isinstance(BaseException(), Exception)
False

Ваши собственные исключения должны наследоваться от Exception, а не BaseException.

person Simeon Visser    schedule 21.11.2014