Catching exception in context manager __enter__()
Is it possible to ensure the
__exit__() method is called even if there is an exception in
>>> 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__ >>>
This is as close as I could get...
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
In hind sight, a context manager might have not been the best design decision
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
To let the program continue on its merry way without executing the context block you need to inspect the context object inside the context block and only do the important stuff if
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"
As far as I can determine, you can't skip the with-block entirely. And note that this context now swallows all exceptions in it. If you wish not to swallow exceptions if
__enter__ succeeds, check
return False if it's
contextlib — Utilities for with-statement contexts — Python 3.8.5 , Context managers inheriting from ContextDecorator have to implement __enter __ and __exit__ as normal. __exit__ retains its optional exception handling even � The return value is the result of the context manager’s own __enter__() method. These context managers may suppress exceptions just as they normally would if used directly as part of a with statement. push (exit) ¶ Adds a context manager’s __exit__() method to the callback stack.
No. If there is the chance that an exception could occur in
__enter__() then you will need to catch it yourself and call a helper function that contains the cleanup code.
You could use
contextlib.ExitStack (not tested):
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
Or an example from the docs:
stack = ExitStack() try: x = stack.enter_context(cm) except Exception: # handle __enter__ exception else: with stack: # Handle normal case
Context Managers & with statement | by Rachit Tayal, Specify the __enter__ & __exit__ context manager methods to by abstracting the try/catch exception handling inside context managers. Trying to understanding how context managers work to catch errors, but more specifically the role of the __enter__() method in a class created to be used as a context
if inheritance or complex subroutines are not required, you can use a shorter way:
from contextlib import contextmanager @contextmanager def test_cm(): try: # dangerous code yield except Exception, err pass # do something
26. Context Managers — Python Tips 0.1 documentation, At the very least a context manager has an __enter__ and __exit__ method defined. Let's make our Let's try handling the exception in the __exit__ method: . class catch_threading_exception: """ Context manager catching threading.Thread exception using: threading.excepthook. Attributes set when an exception is catched: * exc_type * exc_value * exc_traceback * thread: See threading.excepthook() documentation for these attributes. These attributes are deleted at the context manager exit. Usage:
I suggest you follow RAII (resource acquisition is initialization) and use the constructor of your context to do the potentially failing allocation. Then your
__enter__ can simply return self which should never ever raise an exception. If your constructor fails, the exception may be thrown before even entering the with context.
class Foo: def __init__(self): print("init") raise Exception("booh") def __enter__(self): print("enter") return self def __exit__(self, exc_type, exc_val, exc_tb): print("exit") return False with Foo() as f: print("within with")
init Traceback (most recent call last): File "<input>", line 1, in <module> ... raise Exception("booh") Exception: booh
Python context managers � GitHub, _msg = msg def __enter__(self): # time.monotonic() requires Python >= 3.3 self. Context managers can also handle exceptions from inside the block they� Here is a class which can be used as either a decorator or context manager for guarding against the given exceptions. It takes an exception (or a tuple of exceptions) as argument, and if the wrapped code raises that exception, it is re-raised as another exception type (by default RuntimeError). For example:
contextlib – Context manager utilities, python contextlib_api.py __init__() __enter__() Doing work in the context If the context manager can handle the exception, __exit__() should return a true� @@ -3040,6 +3040,18 @@ class catch_unraisable_exception: """ Context manager catching unraisable exception using sys.unraisablehook. If the *object* attribute of the unraisable hook is set and the object is: being finalized, the object is resurrected because the context manager: stores a strong reference to it (cm.unraisable.object).
contextlib — Context Manager Utilities — PyMOTW 3, The __enter__() method can return any object to be associated with a If the context manager can handle the exception, __exit__() should� 29.6.1. Utilities¶. Functions and classes provided: @contextlib.contextmanager¶ This function is a decorator that can be used to define a factory function for with statement context managers, without needing to create a class or separate __enter__() and __exit__() methods.
The Truth About Context Managers In Python, These methods of the context manager are __enter()__ and __exit()__ and are known popularly __exit__() method and Handling Exceptions. I'm often faced with the situation that depending on some command line argument, input might either come from a file or standard input. The same goes for output. I really like how context managers in python 3 work, and therefore try to make all my open calls part of some with statement.