Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4d015a4

Browse files
authored
[3.5] bpo-29692: contextlib.contextmanager may incorrectly unchain RuntimeError (pythonGH-949) (python#1107)
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 00c75e9)
1 parent c0f4240 commit 4d015a4

File tree

3 files changed

+32
-6
lines changed

3 files changed

+32
-6
lines changed

Lib/contextlib.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def __exit__(self, type, value, traceback):
6565
try:
6666
next(self.gen)
6767
except StopIteration:
68-
return
68+
return False
6969
else:
7070
raise RuntimeError("generator didn't stop")
7171
else:
@@ -87,7 +87,7 @@ def __exit__(self, type, value, traceback):
8787
# Likewise, avoid suppressing if a StopIteration exception
8888
# was passed to throw() and later wrapped into a RuntimeError
8989
# (see PEP 479).
90-
if exc.__cause__ is value:
90+
if type is StopIteration and exc.__cause__ is value:
9191
return False
9292
raise
9393
except:
@@ -98,10 +98,10 @@ def __exit__(self, type, value, traceback):
9898
# fixes the impedance mismatch between the throw() protocol
9999
# and the __exit__() protocol.
100100
#
101-
if sys.exc_info()[1] is not value:
102-
raise
103-
else:
104-
raise RuntimeError("generator didn't stop after throw()")
101+
if sys.exc_info()[1] is value:
102+
return False
103+
raise
104+
raise RuntimeError("generator didn't stop after throw()")
105105

106106

107107
def contextmanager(func):

Lib/test/test_contextlib.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,29 @@ def woohoo():
119119
else:
120120
self.fail('StopIteration was suppressed')
121121

122+
def test_contextmanager_do_not_unchain_non_stopiteration_exceptions(self):
123+
@contextmanager
124+
def test_issue29692():
125+
try:
126+
yield
127+
except Exception as exc:
128+
raise RuntimeError('issue29692:Chained') from exc
129+
try:
130+
with test_issue29692():
131+
raise ZeroDivisionError
132+
except Exception as ex:
133+
self.assertIs(type(ex), RuntimeError)
134+
self.assertEqual(ex.args[0], 'issue29692:Chained')
135+
self.assertIsInstance(ex.__cause__, ZeroDivisionError)
136+
137+
try:
138+
with test_issue29692():
139+
raise StopIteration('issue29692:Unchained')
140+
except Exception as ex:
141+
self.assertIs(type(ex), StopIteration)
142+
self.assertEqual(ex.args[0], 'issue29692:Unchained')
143+
self.assertIsNone(ex.__cause__)
144+
122145
def _create_contextmanager_attribs(self):
123146
def attribs(**kw):
124147
def decorate(func):

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ Extension Modules
4848

4949
Library
5050
-------
51+
- bpo-29692: Fixed arbitrary unchaining of RuntimeError exceptions in
52+
contextlib.contextmanager.
53+
Patch by Siddharth Velankar.
5154

5255
- bpo-29998: Pickling and copying ImportError now preserves name and path
5356
attributes.

0 commit comments

Comments
 (0)