8000 [3.6] bpo-29692: contextlib.contextmanager may incorrectly unchain Ru… · python/cpython@9b409ff · GitHub
[go: up one dir, main page]

Skip to content

Commit 9b409ff

Browse files
authored
[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 00c75e9)
1 parent bd1173f commit 9b409ff

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
@@ -31,6 +31,9 @@ Core and Builtins
3131

3232
Library
3333
-------
34+
- bpo-29692: Fixed arbitrary unchaining of RuntimeError exceptions in
35+
contextlib.contextmanager.
36+
Patch by Siddharth Velankar.
3437

3538
- bpo-29998: Pickling and copying ImportError now preserves name and path
3639
attributes.

0 commit comments

Comments
 (0)
0