8000 bpo-29692: contextlib.contextmanager may incorrectly unchain RuntimeE… · python/cpython@00c75e9 · GitHub
[go: up one dir, main page]

Skip to content

Commit 00c75e9

Browse files
soolabettuncoghlan
authored andcommitted
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.
1 parent 6fab78e commit 00c75e9

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
@@ -88,7 +88,7 @@ def __exit__(self, type, value, traceback):
8888
try:
8989
next(self.gen)
9090
except StopIteration:
91-
return
91+
return False
9292
else:
9393
raise RuntimeError("generator didn't stop")
9494
else:
@@ -110,7 +110,7 @@ def __exit__(self, type, value, traceback):
110110
# Likewise, avoid suppressing if a StopIteration exception
111111
# was passed to throw() and later wrapped into a RuntimeError
112112
# (see PEP 479).
113-
if exc.__cause__ is value:
113+
if type is StopIteration and exc.__cause__ is value:
114114
return False
115115
raise
116116
except:
@@ -121,10 +121,10 @@ def __exit__(self, type, value, traceback):
121121
# fixes the impedance mismatch between the throw() protocol
122122
# and the __exit__() protocol.
123123
#
124-
if sys.exc_info()[1] is not value:
125-
raise
126-
else:
127-
raise RuntimeError("generator didn't stop after throw()")
124+
if sys.exc_info()[1] is value:
125+
return False
126+
raise
127+
raise RuntimeError("generator didn't stop after throw()")
128128

129129

130130
def contextmanager(func):

Lib/test/test_contextlib.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,29 @@ def woohoo():
152152
else:
153153
self.fail('StopIteration was suppressed')
154154

155+
def test_contextmanager_do_not_unchain_non_stopiteration_exceptions(self):
156+
@contextmanager
157+
def test_issue29692():
158+
try:
159+
yield
160+
except Exception as exc:
161+
raise RuntimeError('issue29692:Chained') from exc
162+
try:
163+
with test_issue29692():
164+
raise ZeroDivisionError
165+
except Exception as ex:
166+
self.assertIs(type(ex), RuntimeError)
167+
self.assertEqual(ex.args[0], 'issue29692:Chained')
168+
self.assertIsInstance(ex.__cause__, ZeroDivisionError)
169+
170+
try:
171+
with test_issue29692():
172+
raise StopIteration('issue29692:Unchained')
173+
except Exception as ex:
174+
self.assertIs(type(ex), StopIteration)
175+
self.assertEqual(ex.args[0], 'issue29692:Unchained')
176+
self.assertIsNone(ex.__cause__)
177+
155178
def _create_contextmanager_attribs(self):
156179
def attribs(**kw):
157180
def decorate(func):

Misc/NEWS

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

307307
Library
308308
-------
309+
- bpo-29692: Fixed arbitrary unchaining of RuntimeError exceptions in
310+
contextlib.contextmanager.
311+
Patch by Siddharth Velankar.
309312

310313
- bpo-26187: Test that sqlite3 trace callback is not called multiple
311314
times when schema is changing. Indirectly fixed by switching to

0 commit comments

Comments
 (0)
0