[Python-checkins] cpython (merge default -> default): Merge.

stefan.krah python-checkins at python.org
Thu May 31 16:05:06 CEST 2012


http://hg.python.org/cpython/rev/c623f681ad1a
changeset:   77262:c623f681ad1a
parent:      77261:dff6dd248059
parent:      77260:c0c7618762e5
user:        Stefan Krah <skrah at bytereef.org>
date:        Thu May 31 16:03:49 2012 +0200
summary:
  Merge.

files:
  Lib/contextlib.py           |  41 +++++++++---------------
  Lib/test/test_contextlib.py |   6 +++
  Misc/NEWS                   |   8 ++++-
  3 files changed, 28 insertions(+), 27 deletions(-)


diff --git a/Lib/contextlib.py b/Lib/contextlib.py
--- a/Lib/contextlib.py
+++ b/Lib/contextlib.py
@@ -225,32 +225,21 @@
         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
--- a/Lib/test/test_contextlib.py
+++ b/Lib/test/test_contextlib.py
@@ -572,6 +572,12 @@
             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()
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -7,11 +7,17 @@
 
 *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?
 ===================================

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list