Kaydet (Commit) 9b409ff4 authored tarafından Mariatta's avatar Mariatta Kaydeden (comit) GitHub

[3.6] bpo-29692: contextlib.contextmanager may incorrectly unchain RuntimeError (GH-949) (#1105)

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 00c75e9a)
üst bd1173f2
...@@ -88,7 +88,7 @@ class _GeneratorContextManager(ContextDecorator, AbstractContextManager): ...@@ -88,7 +88,7 @@ class _GeneratorContextManager(ContextDecorator, AbstractContextManager):
try: try:
next(self.gen) next(self.gen)
except StopIteration: except StopIteration:
return return False
else: else:
raise RuntimeError("generator didn't stop") raise RuntimeError("generator didn't stop")
else: else:
...@@ -110,7 +110,7 @@ class _GeneratorContextManager(ContextDecorator, AbstractContextManager): ...@@ -110,7 +110,7 @@ class _GeneratorContextManager(ContextDecorator, AbstractContextManager):
# Likewise, avoid suppressing if a StopIteration exception # Likewise, avoid suppressing if a StopIteration exception
# was passed to throw() and later wrapped into a RuntimeError # was passed to throw() and later wrapped into a RuntimeError
# (see PEP 479). # (see PEP 479).
if exc.__cause__ is value: if type is StopIteration and exc.__cause__ is value:
return False return False
raise raise
except: except:
...@@ -121,9 +121,9 @@ class _GeneratorContextManager(ContextDecorator, AbstractContextManager): ...@@ -121,9 +121,9 @@ class _GeneratorContextManager(ContextDecorator, AbstractContextManager):
# fixes the impedance mismatch between the throw() protocol # fixes the impedance mismatch between the throw() protocol
# and the __exit__() protocol. # and the __exit__() protocol.
# #
if sys.exc_info()[1] is not value: if sys.exc_info()[1] is value:
return False
raise raise
else:
raise RuntimeError("generator didn't stop after throw()") raise RuntimeError("generator didn't stop after throw()")
......
...@@ -152,6 +152,29 @@ def woohoo(): ...@@ -152,6 +152,29 @@ def woohoo():
else: else:
self.fail('StopIteration was suppressed') 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 _create_contextmanager_attribs(self):
def attribs(**kw): def attribs(**kw):
def decorate(func): def decorate(func):
......
...@@ -31,6 +31,9 @@ Core and Builtins ...@@ -31,6 +31,9 @@ Core and Builtins
Library 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 - bpo-29998: Pickling and copying ImportError now preserves name and path
attributes. attributes.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment