diff options
author | Stefan Krah <skrah@bytereef.org> | 2012-05-31 14:03:49 (GMT) |
---|---|---|
committer | Stefan Krah <skrah@bytereef.org> | 2012-05-31 14:03:49 (GMT) |
commit | 1ef17954ccc8530a32c359029e48c79af6fb0935 (patch) | |
tree | 7ef5b06fb8ba85d548780b5b11c272a2e201cb32 | |
parent | 5ddbcfc53eb579d27157b539c271534ccabac26a (diff) | |
parent | a5bd2a18ce2bc0910357ff392b373174e0942e3b (diff) | |
download | cpython-1ef17954ccc8530a32c359029e48c79af6fb0935.zip cpython-1ef17954ccc8530a32c359029e48c79af6fb0935.tar.gz cpython-1ef17954ccc8530a32c359029e48c79af6fb0935.tar.bz2 |
Merge.
-rw-r--r-- | Lib/contextlib.py | 41 | ||||
-rw-r--r-- | Lib/test/test_contextlib.py | 6 | ||||
-rw-r--r-- | Misc/NEWS | 8 |
3 files changed, 28 insertions, 27 deletions
diff --git a/Lib/contextlib.py b/Lib/contextlib.py index ead1155..f5232b6 100644 --- a/Lib/contextlib.py +++ b/Lib/contextlib.py @@ -225,32 +225,21 @@ class ExitStack(object): return self def __exit__(self, *exc_details): - if not self._exit_callbacks: - return - # This looks complicated, but it is really just - # setting up a chain of try-expect statements to ensure - # that outer callbacks still get invoked even if an - # inner one throws an exception - def _invoke_next_callback(exc_details): - # Callbacks are removed from the list in FIFO order - # but the recursion means they're invoked in LIFO order - cb = self._exit_callbacks.popleft() - if not self._exit_callbacks: - # Innermost callback is invoked directly - return cb(*exc_details) - # More callbacks left, so descend another level in the stack + # Callbacks are invoked in LIFO order to match the behaviour of + # nested context managers + suppressed_exc = False + while self._exit_callbacks: + cb = self._exit_callbacks.pop() try: - suppress_exc = _invoke_next_callback(exc_details) + if cb(*exc_details): + suppressed_exc = True + exc_details = (None, None, None) except: - suppress_exc = cb(*sys.exc_info()) - # Check if this cb suppressed the inner exception - if not suppress_exc: + new_exc_details = sys.exc_info() + if exc_details != (None, None, None): + # simulate the stack of exceptions by setting the context + new_exc_details[1].__context__ = exc_details[1] + if not self._exit_callbacks: raise - else: - # Check if inner cb suppressed the original exception - if suppress_exc: - exc_details = (None, None, None) - suppress_exc = cb(*exc_details) or suppress_exc - return suppress_exc - # Kick off the recursive chain - return _invoke_next_callback(exc_details) + exc_details = new_exc_details + return suppressed_exc diff --git a/Lib/test/test_contextlib.py b/Lib/test/test_contextlib.py index e5eed21..efa9dcb 100644 --- a/Lib/test/test_contextlib.py +++ b/Lib/test/test_contextlib.py @@ -572,6 +572,12 @@ class TestExitStack(unittest.TestCase): stack.push(lambda *exc: 1/0) stack.push(lambda *exc: {}[1]) + def test_excessive_nesting(self): + # The original implementation would die with RecursionError here + with ExitStack() as stack: + for i in range(10000): + stack.callback(int) + def test_instance_bypass(self): class Example(object): pass cm = Example() @@ -7,11 +7,17 @@ What's New in Python 3.3.0 Beta 1? *Release date: TBD* +Library +------- + +- Issue #14963: Convert contextlib.ExitStack.__exit__ to use an iterative + algorithm (Patch by Alon Horev) + Tests ----- - Issue #14963 (partial): Add test cases for exception handling behaviour - in contextlib.ContextStack (Initial patch by Alon Horev) + in contextlib.ExitStack (Initial patch by Alon Horev) What's New in Python 3.3.0 Alpha 4? =================================== |