Skip to content

Commit 3309eb2

Browse files
authored
Merge branch 'main' into trim-code-object
2 parents 8713010 + b3722ca commit 3309eb2

File tree

7 files changed

+92
-4
lines changed

7 files changed

+92
-4
lines changed

Lib/contextlib.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ def __exit__(self, typ, value, traceback):
173173
isinstance(value, StopIteration)
174174
and exc.__cause__ is value
175175
):
176-
exc.__traceback__ = traceback
176+
value.__traceback__ = traceback
177177
return False
178178
raise
179179
except BaseException as exc:
@@ -228,6 +228,7 @@ async def __aexit__(self, typ, value, traceback):
228228
except RuntimeError as exc:
229229
# Don't re-raise the passed in exception. (issue27122)
230230
if exc is value:
231+
exc.__traceback__ = traceback
231232
return False
232233
# Avoid suppressing if a Stop(Async)Iteration exception
233234
# was passed to athrow() and later wrapped into a RuntimeError
@@ -239,6 +240,7 @@ async def __aexit__(self, typ, value, traceback):
239240
isinstance(value, (StopIteration, StopAsyncIteration))
240241
and exc.__cause__ is value
241242
):
243+
value.__traceback__ = traceback
242244
return False
243245
raise
244246
except BaseException as exc:
@@ -250,6 +252,7 @@ async def __aexit__(self, typ, value, traceback):
250252
# and the __exit__() protocol.
251253
if exc is not value:
252254
raise
255+
exc.__traceback__ = traceback
253256
return False
254257
raise RuntimeError("generator didn't stop after athrow()")
255258

Lib/test/test_contextlib.py

+27-3
Original file line numberDiff line numberDiff line change
@@ -104,15 +104,39 @@ def f():
104104
self.assertEqual(frames[0].line, '1/0')
105105

106106
# Repeat with RuntimeError (which goes through a different code path)
107+
class RuntimeErrorSubclass(RuntimeError):
108+
pass
109+
107110
try:
108111
with f():
109-
raise NotImplementedError(42)
110-
except NotImplementedError as e:
112+
raise RuntimeErrorSubclass(42)
113+
except RuntimeErrorSubclass as e:
111114
frames = traceback.extract_tb(e.__traceback__)
112115

113116
self.assertEqual(len(frames), 1)
114117
self.assertEqual(frames[0].name, 'test_contextmanager_traceback')
115-
self.assertEqual(frames[0].line, 'raise NotImplementedError(42)')
118+
self.assertEqual(frames[0].line, 'raise RuntimeErrorSubclass(42)')
119+
120+
class StopIterationSubclass(StopIteration):
121+
pass
122+
123+
for stop_exc in (
124+
StopIteration('spam'),
125+
StopIterationSubclass('spam'),
126+
):
127+
with self.subTest(type=type(stop_exc)):
128+
try:
129+
with f():
130+
raise stop_exc
131+
except type(stop_exc) as e:
132+
self.assertIs(e, stop_exc)
133+
frames = traceback.extract_tb(e.__traceback__)
134+
else:
135+
self.fail(f'{stop_exc} was suppressed')
136+
137+
self.assertEqual(len(frames), 1)
138+
self.assertEqual(frames[0].name, 'test_contextmanager_traceback')
139+
self.assertEqual(frames[0].line, 'raise stop_exc')
116140

117141
def test_contextmanager_no_reraise(self):
118142
@contextmanager

Lib/test/test_contextlib_async.py

+57
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import functools
66
from test import support
77
import unittest
8+
import traceback
89

910
from test.test_contextlib import TestBaseExitStack
1011

@@ -125,6 +126,62 @@ async def woohoo():
125126
raise ZeroDivisionError()
126127
self.assertEqual(state, [1, 42, 999])
127128

129+
@_async_test
130+
async def test_contextmanager_traceback(self):
131+
@asynccontextmanager
132+
async def f():
133+
yield
134+
135+
try:
136+
async with f():
137+
1/0
138+
except ZeroDivisionError as e:
139+
frames = traceback.extract_tb(e.__traceback__)
140+
141+
self.assertEqual(len(frames), 1)
142+
self.assertEqual(frames[0].name, 'test_contextmanager_traceback')
143+
self.assertEqual(frames[0].line, '1/0')
144+
145+
# Repeat with RuntimeError (which goes through a different code path)
146+
class RuntimeErrorSubclass(RuntimeError):
147+
pass
148+
149+
try:
150+
async with f():
151+
raise RuntimeErrorSubclass(42)
152+
except RuntimeErrorSubclass as e:
153+
frames = traceback.extract_tb(e.__traceback__)
154+
155+
self.assertEqual(len(frames), 1)
156+
self.assertEqual(frames[0].name, 'test_contextmanager_traceback')
157+
self.assertEqual(frames[0].line, 'raise RuntimeErrorSubclass(42)')
158+
159+
class StopIterationSubclass(StopIteration):
160+
pass
161+
162+
class StopAsyncIterationSubclass(StopAsyncIteration):
163+
pass
164+
165+
for stop_exc in (
166+
StopIteration('spam'),
167+
StopAsyncIteration('ham'),
168+
StopIterationSubclass('spam'),
169+
StopAsyncIterationSubclass('spam')
170+
):
171+
with self.subTest(type=type(stop_exc)):
172+
try:
173+
async with f():
174+
raise stop_exc
175+
except type(stop_exc) as e:
176+
self.assertIs(e, stop_exc)
177+
frames = traceback.extract_tb(e.__traceback__)
178+
else:
179+
self.fail(f'{stop_exc} was suppressed')
180+
181+
self.assertEqual(len(frames), 1)
182+
self.assertEqual(frames[0].name, 'test_contextmanager_traceback')
183+
self.assertEqual(frames[0].line, 'raise stop_exc')
184+
128185
@_async_test
129186
async def test_contextmanager_no_reraise(self):
130187
@asynccontextmanager

Misc/ACKS

+1
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,7 @@ Hans de Graaff
645645
Tim Graham
646646
Kim Gräsman
647647
Alex Grönholm
648+
Thomas Grainger
648649
Nathaniel Gray
649650
Eddy De Greef
650651
Duane Griffin
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a 3.11 regression in :func:`~contextlib.asynccontextmanager`, which caused it to propagate exceptions with incorrect tracebacks and fix a 3.11 regression in :func:`~contextlib.contextmanager`, which caused it to propagate exceptions with incorrect tracebacks for :exc:`StopIteration`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a bug that caused an :exc:`AttributeError` to be raised in ``python-gdb.py`` when ``py-locals`` is used without a frame.

Tools/gdb/libpython.py

+1
Original file line numberDiff line numberDiff line change
@@ -2108,6 +2108,7 @@ def invoke(self, args, from_tty):
21082108
while True:
21092109
if not pyop_frame:
21102110
print(UNABLE_READ_INFO_PYTHON_FRAME)
2111+
break
21112112
if pyop_frame.is_shim():
21122113
break
21132114

0 commit comments

Comments
 (0)