Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1a33b2f

Browse files
committed
Close #19092: ExitStack now reraises exceptions from __exit__
Report and patch by Hrvoje Nikšić
1 parent 2ff2190 commit 1a33b2f

4 files changed

Lines changed: 57 additions & 4 deletions

File tree

Lib/contextlib.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,8 @@ def __enter__(self):
225225
return self
226226

227227
def __exit__(self, *exc_details):
228+
received_exc = exc_details[0] is not None
229+
228230
# We manipulate the exception state so it behaves as though
229231
# we were actually nesting multiple with statements
230232
frame_exc = sys.exc_info()[1]
@@ -239,17 +241,27 @@ def _fix_exception_context(new_exc, old_exc):
239241
# Callbacks are invoked in LIFO order to match the behaviour of
240242
# nested context managers
241243
suppressed_exc = False
244+
pending_raise = False
242245
while self._exit_callbacks:
243246
cb = self._exit_callbacks.pop()
244247
try:
245248
if cb(*exc_details):
246249
suppressed_exc = True
250+
pending_raise = False
247251
exc_details = (None, None, None)
248252
except:
249253
new_exc_details = sys.exc_info()
250254
# simulate the stack of exceptions by setting the context
251255
_fix_exception_context(new_exc_details[1], exc_details[1])
252-
if not self._exit_callbacks:
253-
raise
256+
pending_raise = True
254257
exc_details = new_exc_details
255-
return suppressed_exc
258+
if pending_raise:
259+
try:
260+
# bare "raise exc_details[1]" replaces our carefully
261+
# set-up context
262+
fixed_ctx = exc_details[1].__context__
263+
raise exc_details[1]
264+
except BaseException:
265+
exc_details[1].__context__ = fixed_ctx
266+
raise
267+
return received_exc and suppressed_exc

Lib/test/test_contextlib.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,43 @@ def suppress_exc(*exc_details):
573573
self.assertIsInstance(inner_exc, ValueError)
574574
self.assertIsInstance(inner_exc.__context__, ZeroDivisionError)
575575

576+
def test_exit_exception_non_suppressing(self):
577+
# http://bugs.python.org/issue19092
578+
def raise_exc(exc):
579+
raise exc
580+
581+
def suppress_exc(*exc_details):
582+
return True
583+
584+
try:
585+
with ExitStack() as stack:
586+
stack.callback(lambda: None)
587+
stack.callback(raise_exc, IndexError)
588+
except Exception as exc:
589+
self.assertIsInstance(exc, IndexError)
590+
else:
591+
self.fail("Expected IndexError, but no exception was raised")
592+
593+
try:
594+
with ExitStack() as stack:
595+
stack.callback(raise_exc, KeyError)
596+
stack.push(suppress_exc)
597+
stack.callback(raise_exc, IndexError)
598+
except Exception as exc:
599+
self.assertIsInstance(exc, KeyError)
600+
else:
601+
self.fail("Expected KeyError, but no exception was raised")
602+
603+
def test_body_exception_suppress(self):
604+
def suppress_exc(*exc_details):
605+
return True
606+
try:
607+
with ExitStack() as stack:
608+
stack.push(suppress_exc)
609+
1/0
610+
except IndexError as exc:
611+
self.fail("Expected no exception, got IndexError")
612+
576613
def test_exit_exception_chaining_suppress(self):
577614
with ExitStack() as stack:
578615
stack.push(lambda *exc: True)

Misc/ACKS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -882,7 +882,7 @@ Samuel Nicolary
882882
Jonathan Niehof
883883
Gustavo Niemeyer
884884
Oscar Nierstrasz
885-
Hrvoje Niksic
885+
Hrvoje Nikšić
886886
Gregory Nofi
887887
Jesse Noller
888888
Bill Noon

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ Core and Builtins
7171
Library
7272
-------
7373

74+
- Issue #19092: contextlib.ExitStack now correctly reraises exceptions
75+
from the __exit__ callbacks of inner context managers (Patch by Hrvoje
76+
Nikšić)
77+
7478
- Issue #12641: Avoid passing "-mno-cygwin" to the mingw32 compiler, except
7579
when necessary. Patch by Oscar Benjamin.
7680

0 commit comments

Comments
 (0)