bpo-29692: contextlib.contextmanager may incorrectly unchain RuntimeError (GH-949)
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.
This commit is contained in:
parent
6fab78e902
commit
00c75e9a45
|
@ -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):
|
||||||
# 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,10 +121,10 @@ 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:
|
||||||
raise
|
return False
|
||||||
else:
|
raise
|
||||||
raise RuntimeError("generator didn't stop after throw()")
|
raise RuntimeError("generator didn't stop after throw()")
|
||||||
|
|
||||||
|
|
||||||
def contextmanager(func):
|
def contextmanager(func):
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -306,6 +306,9 @@ Extension Modules
|
||||||
|
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
- bpo-29692: Fixed arbitrary unchaining of RuntimeError exceptions in
|
||||||
|
contextlib.contextmanager.
|
||||||
|
Patch by Siddharth Velankar.
|
||||||
|
|
||||||
- bpo-26187: Test that sqlite3 trace callback is not called multiple
|
- bpo-26187: Test that sqlite3 trace callback is not called multiple
|
||||||
times when schema is changing. Indirectly fixed by switching to
|
times when schema is changing. Indirectly fixed by switching to
|
||||||
|
|
Loading…
Reference in New Issue