Skip to content

Commit e6f4631

Browse files
committed
Merge #19092 from 3.3
2 parents c13516b + 1a33b2f commit e6f4631

File tree

4 files changed

+57
-4
lines changed

4 files changed

+57
-4
lines changed

Lib/contextlib.py

+15-3
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,8 @@ def __enter__(self):
237237
return self
238238

239239
def __exit__(self, *exc_details):
240+
received_exc = exc_details[0] is not None
241+
240242
# We manipulate the exception state so it behaves as though
241243
# we were actually nesting multiple with statements
242244
frame_exc = sys.exc_info()[1]
@@ -251,17 +253,27 @@ def _fix_exception_context(new_exc, old_exc):
251253
# Callbacks are invoked in LIFO order to match the behaviour of
252254
# nested context managers
253255
suppressed_exc = False
256+
pending_raise = False
254257
while self._exit_callbacks:
255258
cb = self._exit_callbacks.pop()
256259
try:
257260
if cb(*exc_details):
258261
suppressed_exc = True
262+
pending_raise = False
259263
exc_details = (None, None, None)
260264
except:
261265
new_exc_details = sys.exc_info()
262266
# simulate the stack of exceptions by setting the context
263267
_fix_exception_context(new_exc_details[1], exc_details[1])
264-
if not self._exit_callbacks:
265-
raise
268+
pending_raise = True
266269
exc_details = new_exc_details
267-
return suppressed_exc
270+
if pending_raise:
271+
try:
272+
# bare "raise exc_details[1]" replaces our carefully
273+
# set-up context
274+
fixed_ctx = exc_details[1].__context__
275+
raise exc_details[1]
276+
except BaseException:
277+
exc_details[1].__context__ = fixed_ctx
278+
raise
279+
return received_exc and suppressed_exc

Lib/test/test_contextlib.py

+37
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

+1-1
Original file line numberDiff line numberDiff line change
@@ -912,7 +912,7 @@ Samuel Nicolary
912912
Jonathan Niehof
913913
Gustavo Niemeyer
914914
Oscar Nierstrasz
915-
Hrvoje Niksic
915+
Hrvoje Nikšić
916916
Gregory Nofi
917917
Jesse Noller
918918
Bill Noon

Misc/NEWS

+4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ Core and Builtins
1313
Library
1414
-------
1515

16+
- Issue #19092: contextlib.ExitStack now correctly reraises exceptions
17+
from the __exit__ callbacks of inner context managers (Patch by Hrvoje
18+
Nikšić)
19+
1620
- Issue #12641: Avoid passing "-mno-cygwin" to the mingw32 compiler, except
1721
when necessary. Patch by Oscar Benjamin.
1822

0 commit comments

Comments
 (0)