@@ -225,32 +225,21 @@ def __enter__(self):
225225 return self
226226
227227 def __exit__ (self , * exc_details ):
228- if not self ._exit_callbacks :
229- return
230- # This looks complicated, but it is really just
231- # setting up a chain of try-expect statements to ensure
232- # that outer callbacks still get invoked even if an
233- # inner one throws an exception
234- def _invoke_next_callback (exc_details ):
235- # Callbacks are removed from the list in FIFO order
236- # but the recursion means they're invoked in LIFO order
237- cb = self ._exit_callbacks .popleft ()
238- if not self ._exit_callbacks :
239- # Innermost callback is invoked directly
240- return cb (* exc_details )
241- # More callbacks left, so descend another level in the stack
228+ # Callbacks are invoked in LIFO order to match the behaviour of
229+ # nested context managers
230+ suppressed_exc = False
231+ while self ._exit_callbacks :
232+ cb = self ._exit_callbacks .pop ()
242233 try :
243- suppress_exc = _invoke_next_callback (exc_details )
234+ if cb (* exc_details ):
235+ suppressed_exc = True
236+ exc_details = (None , None , None )
244237 except :
245- suppress_exc = cb (* sys .exc_info ())
246- # Check if this cb suppressed the inner exception
247- if not suppress_exc :
238+ new_exc_details = sys .exc_info ()
239+ if exc_details != (None , None , None ):
240+ # simulate the stack of exceptions by setting the context
241+ new_exc_details [1 ].__context__ = exc_details [1 ]
242+ if not self ._exit_callbacks :
248243 raise
249- else :
250- # Check if inner cb suppressed the original exception
251- if suppress_exc :
252- exc_details = (None , None , None )
253- suppress_exc = cb (* exc_details ) or suppress_exc
254- return suppress_exc
255- # Kick off the recursive chain
256- return _invoke_next_callback (exc_details )
244+ exc_details = new_exc_details
245+ return suppressed_exc
0 commit comments