[Python-checkins] cpython: Issue #14963: Added test cases for contextlib.ExitStack exception handling

nick.coghlan python-checkins at python.org
Thu May 31 15:49:44 CEST 2012


http://hg.python.org/cpython/rev/fc73e6ea9e73
changeset:   77259:fc73e6ea9e73
user:        Nick Coghlan <ncoghlan at gmail.com>
date:        Thu May 31 23:49:26 2012 +1000
summary:
  Issue #14963: Added test cases for contextlib.ExitStack exception handling behaviour (Initial patch by Alon Horev)

files:
  Lib/test/test_contextlib.py |  89 +++++++++++++++++++++++++
  Misc/ACKS                   |   1 +
  Misc/NEWS                   |  11 +++
  3 files changed, 101 insertions(+), 0 deletions(-)


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
@@ -483,6 +483,95 @@
         new_stack.close()
         self.assertEqual(result, [1, 2, 3])
 
+    def test_exit_raise(self):
+        with self.assertRaises(ZeroDivisionError):
+            with ExitStack() as stack:
+                stack.push(lambda *exc: False)
+                1/0
+
+    def test_exit_suppress(self):
+        with ExitStack() as stack:
+            stack.push(lambda *exc: True)
+            1/0
+
+    def test_exit_exception_chaining_reference(self):
+        # Sanity check to make sure that ExitStack chaining matches
+        # actual nested with statements
+        class RaiseExc:
+            def __init__(self, exc):
+                self.exc = exc
+            def __enter__(self):
+                return self
+            def __exit__(self, *exc_details):
+                raise self.exc
+
+        class SuppressExc:
+            def __enter__(self):
+                return self
+            def __exit__(self, *exc_details):
+                type(self).saved_details = exc_details
+                return True
+
+        try:
+            with RaiseExc(IndexError):
+                with RaiseExc(KeyError):
+                    with RaiseExc(AttributeError):
+                        with SuppressExc():
+                            with RaiseExc(ValueError):
+                                1 / 0
+        except IndexError as exc:
+            self.assertIsInstance(exc.__context__, KeyError)
+            self.assertIsInstance(exc.__context__.__context__, AttributeError)
+            # Inner exceptions were suppressed
+            self.assertIsNone(exc.__context__.__context__.__context__)
+        else:
+            self.fail("Expected IndexError, but no exception was raised")
+        # Check the inner exceptions
+        inner_exc = SuppressExc.saved_details[1]
+        self.assertIsInstance(inner_exc, ValueError)
+        self.assertIsInstance(inner_exc.__context__, ZeroDivisionError)
+
+    def test_exit_exception_chaining(self):
+        # Ensure exception chaining matches the reference behaviour
+        def raise_exc(exc):
+            raise exc
+
+        saved_details = None
+        def suppress_exc(*exc_details):
+            nonlocal saved_details
+            saved_details = exc_details
+            return True
+
+        try:
+            with ExitStack() as stack:
+                stack.callback(raise_exc, IndexError)
+                stack.callback(raise_exc, KeyError)
+                stack.callback(raise_exc, AttributeError)
+                stack.push(suppress_exc)
+                stack.callback(raise_exc, ValueError)
+                1 / 0
+        except IndexError as exc:
+            self.assertIsInstance(exc.__context__, KeyError)
+            self.assertIsInstance(exc.__context__.__context__, AttributeError)
+            # Inner exceptions were suppressed, but the with statement
+            # cleanup code adds the one from the body back in as the
+            # context of the exception raised by the outer callbacks
+            # See http://bugs.python.org/issue14969
+            suite_exc = exc.__context__.__context__.__context__
+            self.assertIsInstance(suite_exc, ZeroDivisionError)
+        else:
+            self.fail("Expected IndexError, but no exception was raised")
+        # Check the inner exceptions
+        inner_exc = saved_details[1]
+        self.assertIsInstance(inner_exc, ValueError)
+        self.assertIsInstance(inner_exc.__context__, ZeroDivisionError)
+
+    def test_exit_exception_chaining_suppress(self):
+        with ExitStack() as stack:
+            stack.push(lambda *exc: True)
+            stack.push(lambda *exc: 1/0)
+            stack.push(lambda *exc: {}[1])
+
     def test_instance_bypass(self):
         class Example(object): pass
         cm = Example()
diff --git a/Misc/ACKS b/Misc/ACKS
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -460,6 +460,7 @@
 Brian Hooper
 Randall Hopper
 Nadav Horesh
+Alon Horev
 Jan Hosang
 Ken Howard
 Brad Howes
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -2,6 +2,17 @@
 Python News
 +++++++++++
 
+What's New in Python 3.3.0 Beta 1?
+===================================
+
+*Release date: TBD*
+
+Tests
+-----
+
+- Issue #14963 (partial): Add test cases for exception handling behaviour
+  in contextlib.ContextStack (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