Merge #19092 from 3.3

This commit is contained in:
Nick Coghlan 2013-10-01 23:28:00 +10:00
commit e6f4631f08
4 changed files with 57 additions and 4 deletions

View file

@ -237,6 +237,8 @@ def __enter__(self):
return self return self
def __exit__(self, *exc_details): def __exit__(self, *exc_details):
received_exc = exc_details[0] is not None
# We manipulate the exception state so it behaves as though # We manipulate the exception state so it behaves as though
# we were actually nesting multiple with statements # we were actually nesting multiple with statements
frame_exc = sys.exc_info()[1] frame_exc = sys.exc_info()[1]
@ -251,17 +253,27 @@ def _fix_exception_context(new_exc, old_exc):
# Callbacks are invoked in LIFO order to match the behaviour of # Callbacks are invoked in LIFO order to match the behaviour of
# nested context managers # nested context managers
suppressed_exc = False suppressed_exc = False
pending_raise = False
while self._exit_callbacks: while self._exit_callbacks:
cb = self._exit_callbacks.pop() cb = self._exit_callbacks.pop()
try: try:
if cb(*exc_details): if cb(*exc_details):
suppressed_exc = True suppressed_exc = True
pending_raise = False
exc_details = (None, None, None) exc_details = (None, None, None)
except: except:
new_exc_details = sys.exc_info() new_exc_details = sys.exc_info()
# simulate the stack of exceptions by setting the context # simulate the stack of exceptions by setting the context
_fix_exception_context(new_exc_details[1], exc_details[1]) _fix_exception_context(new_exc_details[1], exc_details[1])
if not self._exit_callbacks: pending_raise = True
raise
exc_details = new_exc_details exc_details = new_exc_details
return suppressed_exc if pending_raise:
try:
# bare "raise exc_details[1]" replaces our carefully
# set-up context
fixed_ctx = exc_details[1].__context__
raise exc_details[1]
except BaseException:
exc_details[1].__context__ = fixed_ctx
raise
return received_exc and suppressed_exc

View file

@ -573,6 +573,43 @@ def suppress_exc(*exc_details):
self.assertIsInstance(inner_exc, ValueError) self.assertIsInstance(inner_exc, ValueError)
self.assertIsInstance(inner_exc.__context__, ZeroDivisionError) self.assertIsInstance(inner_exc.__context__, ZeroDivisionError)
def test_exit_exception_non_suppressing(self):
# http://bugs.python.org/issue19092
def raise_exc(exc):
raise exc
def suppress_exc(*exc_details):
return True
try:
with ExitStack() as stack:
stack.callback(lambda: None)
stack.callback(raise_exc, IndexError)
except Exception as exc:
self.assertIsInstance(exc, IndexError)
else:
self.fail("Expected IndexError, but no exception was raised")
try:
with ExitStack() as stack:
stack.callback(raise_exc, KeyError)
stack.push(suppress_exc)
stack.callback(raise_exc, IndexError)
except Exception as exc:
self.assertIsInstance(exc, KeyError)
else:
self.fail("Expected KeyError, but no exception was raised")
def test_body_exception_suppress(self):
def suppress_exc(*exc_details):
return True
try:
with ExitStack() as stack:
stack.push(suppress_exc)
1/0
except IndexError as exc:
self.fail("Expected no exception, got IndexError")
def test_exit_exception_chaining_suppress(self): def test_exit_exception_chaining_suppress(self):
with ExitStack() as stack: with ExitStack() as stack:
stack.push(lambda *exc: True) stack.push(lambda *exc: True)

View file

@ -912,7 +912,7 @@ Samuel Nicolary
Jonathan Niehof Jonathan Niehof
Gustavo Niemeyer Gustavo Niemeyer
Oscar Nierstrasz Oscar Nierstrasz
Hrvoje Niksic Hrvoje Nikšić
Gregory Nofi Gregory Nofi
Jesse Noller Jesse Noller
Bill Noon Bill Noon

View file

@ -13,6 +13,10 @@ Core and Builtins
Library Library
------- -------
- Issue #19092: contextlib.ExitStack now correctly reraises exceptions
from the __exit__ callbacks of inner context managers (Patch by Hrvoje
Nikšić)
- Issue #12641: Avoid passing "-mno-cygwin" to the mingw32 compiler, except - Issue #12641: Avoid passing "-mno-cygwin" to the mingw32 compiler, except
when necessary. Patch by Oscar Benjamin. when necessary. Patch by Oscar Benjamin.