python - statement - Detectando una excepción en el administrador de contexto__enter__()
with python 3 (5)
Me gusta esto:
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
Para permitir que el programa continúe su camino alegre sin ejecutar el bloque de contexto, debe inspeccionar el objeto de contexto dentro del bloque de contexto y solo hacer las cosas importantes si __enter__
tuvo éxito.
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"
Por lo que puedo determinar, no puedes omitir el bloque completo por completo. Y tenga en cuenta que este contexto ahora se traga todas las excepciones en él. Si no desea tragar excepciones si __enter__
tiene éxito, verifique self.enter_ok
en __exit__
y return False
si es True
.
¿Es posible garantizar que se __exit__()
método __exit__()
incluso si hay una excepción en __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__
>>>
Editar
Esto es lo más cerca que pude conseguir ...
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
En retrospectiva, un administrador de contexto podría no haber sido la mejor decisión de diseño
No. Si existe la posibilidad de que se produzca una excepción en __enter__()
, deberá capturarlo usted mismo y llamar a una función auxiliar que contenga el código de limpieza.
Podría usar contextlib.ExitStack
(no probado):
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
O un ejemplo de la documentación :
stack = ExitStack()
try:
x = stack.enter_context(cm)
except Exception:
# handle __enter__ exception
else:
with stack:
# Handle normal case
Si la herencia o las subrutinas complejas no son necesarias, puede utilizar una forma más corta:
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
Lo he hecho así. Llama a __exit __ () con el error como argumento. Si args [0] contiene un error, vuelve a aparecer la excepción después de ejecutar el código de limpieza.