Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Catching exception in context manager __enter__()

Is it possible to ensure the __exit__() method is called even if there is an exception in __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__ >>>  

Edit

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

like image 576
tMC Avatar asked Oct 25 '12 18:10

tMC


People also ask

What does __ enter __ do in Python?

__enter__ and [__exit__] both are methods that are invoked on entry to and exit from the body of "the with statement" (PEP 343) and implementation of both is called context manager.

What actions are guaranteed by a context manager?

Context managers allow you to allocate and release resources precisely when you want to. The most widely used example of context managers is the with statement. Suppose you have two related operations which you'd like to execute as a pair, with a block of code in between.

What is Contextlib?

The contextlib module of Python's standard library provides utilities for resource allocation to the with statement. The with statement in Python is used for resource management and exception handling. Therefore, it serves as a good Context Manager.

What is ExitStack?

An ExitStack is (as the name suggests) a stack of clean-up functions. Adding a callback to the stack is the equivalent of calling Go's defer statement. However, clean-up functions are not executed when the function returns, but when execution leaves the with block - and until then, the stack can also be emptied again.


2 Answers

Like this:

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 __enter__ succeeded.

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 self.enter_ok in __exit__ and return False if it's True.

like image 189
Lauritz V. Thaulow Avatar answered Oct 08 '22 11:10

Lauritz V. Thaulow


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.

like image 43
Ignacio Vazquez-Abrams Avatar answered Oct 08 '22 11:10

Ignacio Vazquez-Abrams