]> granicus.if.org Git - python/commitdiff
[3.6] bpo-29692: contextlib.contextmanager may incorrectly unchain RuntimeError ...
authorMariatta <Mariatta@users.noreply.github.com>
Thu, 13 Apr 2017 09:50:21 +0000 (02:50 -0700)
committerGitHub <noreply@github.com>
Thu, 13 Apr 2017 09:50:21 +0000 (02:50 -0700)
contextlib._GeneratorContextManager.__exit__ includes a special case to deal with
PEP 479 RuntimeErrors created when `StopIteration` is thrown into the context
manager body.

Previously this check was too permissive, and undid one level of chaining on *all*
RuntimeError instances, not just those that wrapped a StopIteration instance.
(cherry picked from commit 00c75e9a45ff0366c185e9e8a2e23af5a35481b0)

Lib/contextlib.py
Lib/test/test_contextlib.py
Misc/NEWS

index e91cf460e53bfde9ea55665d15bbd161699d24a5..5e47054954ba5a27d08a017d311d5ba1d338ae94 100644 (file)
@@ -88,7 +88,7 @@ class _GeneratorContextManager(ContextDecorator, AbstractContextManager):
             try:
                 next(self.gen)
             except StopIteration:
-                return
+                return False
             else:
                 raise RuntimeError("generator didn't stop")
         else:
@@ -110,7 +110,7 @@ class _GeneratorContextManager(ContextDecorator, AbstractContextManager):
                 # Likewise, avoid suppressing if a StopIteration exception
                 # was passed to throw() and later wrapped into a RuntimeError
                 # (see PEP 479).
-                if exc.__cause__ is value:
+                if type is StopIteration and exc.__cause__ is value:
                     return False
                 raise
             except:
@@ -121,10 +121,10 @@ class _GeneratorContextManager(ContextDecorator, AbstractContextManager):
                 # fixes the impedance mismatch between the throw() protocol
                 # and the __exit__() protocol.
                 #
-                if sys.exc_info()[1] is not value:
-                    raise
-            else:
-                raise RuntimeError("generator didn't stop after throw()")
+                if sys.exc_info()[1] is value:
+                    return False
+                raise
+            raise RuntimeError("generator didn't stop after throw()")
 
 
 def contextmanager(func):
index c04c804af5704703872a4490700be52b443aa12f..b1a467d952da0fbb511c2d596dcd7d946f5a1e20 100644 (file)
@@ -152,6 +152,29 @@ def woohoo():
         else:
             self.fail('StopIteration was suppressed')
 
+    def test_contextmanager_do_not_unchain_non_stopiteration_exceptions(self):
+        @contextmanager
+        def test_issue29692():
+            try:
+                yield
+            except Exception as exc:
+                raise RuntimeError('issue29692:Chained') from exc
+        try:
+            with test_issue29692():
+                raise ZeroDivisionError
+        except Exception as ex:
+            self.assertIs(type(ex), RuntimeError)
+            self.assertEqual(ex.args[0], 'issue29692:Chained')
+            self.assertIsInstance(ex.__cause__, ZeroDivisionError)
+
+        try:
+            with test_issue29692():
+                raise StopIteration('issue29692:Unchained')
+        except Exception as ex:
+            self.assertIs(type(ex), StopIteration)
+            self.assertEqual(ex.args[0], 'issue29692:Unchained')
+            self.assertIsNone(ex.__cause__)
+
     def _create_contextmanager_attribs(self):
         def attribs(**kw):
             def decorate(func):
index dd1fa12d7c0be8e1bd3680c7fa15300a662985c5..6e2db58a92f451973c10f901ee65403fe3ef718e 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -31,6 +31,9 @@ Core and Builtins
 
 Library
 -------
+- bpo-29692: Fixed arbitrary unchaining of RuntimeError exceptions in 
+  contextlib.contextmanager.
+  Patch by Siddharth Velankar.
 
 - bpo-29998: Pickling and copying ImportError now preserves name and path
   attributes.