with statement python3 open manager español context python exception python-2.7 with-statement contextmanager

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

Ver contextlib2 en Python <3.3 .


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.