Перехват исключения в context manager enter ()
возможно ли обеспечить __exit__()
метод вызывается, даже если есть исключение в __enter__()
?
>>> class TstContx(object):
... def __enter__(self):
... raise Exception('Oops in __enter__')
...
... def __exit__(self, e_typ, e_val, trcbak):
... print "This isn't running"
...
>>> with TstContx():
... pass
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __enter__
Exception: Oops in __enter__
>>>
редактировать
это так близко, как я мог сделать...
class TstContx(object):
def __enter__(self):
try:
# __enter__ code
except Exception as e
self.init_exc = e
return self
def __exit__(self, e_typ, e_val, trcbak):
if all((e_typ, e_val, trcbak)):
raise e_typ, e_val, trcbak
# __exit__ code
with TstContx() as tc:
if hasattr(tc, 'init_exc'): raise tc.init_exc
# code in context
на заднем плане, менеджер контекста, возможно, не было лучшим дизайнерским решением
5 ответов
такой:
import sys
class Context(object):
def __enter__(self):
try:
raise Exception("Oops in __enter__")
except:
# Swallow exception if __exit__ returns a True value
if self.__exit__(*sys.exc_info()):
pass
else:
raise
def __exit__(self, e_typ, e_val, trcbak):
print "Now it's running"
with Context():
pass
чтобы программа продолжила свой веселый путь без выполнения контекстного блока, вам нужно проверить объект контекста внутри контекстного блока и сделать только важные вещи, если __enter__
удалось.
class Context(object):
def __init__(self):
self.enter_ok = True
def __enter__(self):
try:
raise Exception("Oops in __enter__")
except:
if self.__exit__(*sys.exc_info()):
self.enter_ok = False
else:
raise
return self
def __exit__(self, e_typ, e_val, trcbak):
print "Now this runs twice"
return True
with Context() as c:
if c.enter_ok:
print "Only runs if enter succeeded"
print "Execution continues"
насколько я могу определить, вы не можете полностью пропустить блок with. И обратите внимание, что этот контекст теперь проглатывает все исключения в нем. Если вы не хотите глотать исключения, если __enter__
завершается успешно, проверить self.enter_ok
в __exit__
и return False
если это True
.
нет. Если есть вероятность, что исключение может произойти в __enter__()
тогда вам нужно будет поймать его самостоятельно и вызвать вспомогательную функцию, содержащую код очистки.
можно использовать contextlib.ExitStack
(не проверял):
with ExitStack() as stack:
cm = TstContx()
stack.push(cm) # ensure __exit__ is called
with ctx:
stack.pop_all() # __enter__ succeeded, don't call __exit__ callback
или например документы:
stack = ExitStack()
try:
x = stack.enter_context(cm)
except Exception:
# handle __enter__ exception
else:
with stack:
# Handle normal case
посмотреть contextlib2 на Python .
если наследование или сложные подпрограммы не требуются, вы можете использовать более короткий путь:
from contextlib import contextmanager
@contextmanager
def test_cm():
try:
# dangerous code
yield
except Exception, err
pass # do something
class MyContext:
def __enter__(self):
try:
pass
# exception-raising code
except Exception as e:
self.__exit__(e)
def __exit__(self, *args):
# clean up code ...
if args[0]:
raise
Я сделал это так. Он вызывает __exit__ () с ошибкой в качестве аргумента. Если args[0] содержит ошибку, она перезапускает исключение после выполнения кода очистки.