Skip to content

Commit ff9ac58

Browse files
authored
[3.10] gh-95882: Add tests for traceback from contextlib context managers (GH-95883) (#100715)
1 parent 242836c commit ff9ac58

File tree

2 files changed

+108
-0
lines changed

2 files changed

+108
-0
lines changed

Lib/test/test_contextlib.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,56 @@ def woohoo():
8787
raise ZeroDivisionError()
8888
self.assertEqual(state, [1, 42, 999])
8989

90+
def test_contextmanager_traceback(self):
91+
@contextmanager
92+
def f():
93+
yield
94+
95+
try:
96+
with f():
97+
1/0
98+
except ZeroDivisionError as e:
99+
frames = traceback.extract_tb(e.__traceback__)
100+
101+
self.assertEqual(len(frames), 1)
102+
self.assertEqual(frames[0].name, 'test_contextmanager_traceback')
103+
self.assertEqual(frames[0].line, '1/0')
104+
105+
# Repeat with RuntimeError (which goes through a different code path)
106+
class RuntimeErrorSubclass(RuntimeError):
107+
pass
108+
109+
try:
110+
with f():
111+
raise RuntimeErrorSubclass(42)
112+
except RuntimeErrorSubclass as e:
113+
frames = traceback.extract_tb(e.__traceback__)
114+
115+
self.assertEqual(len(frames), 1)
116+
self.assertEqual(frames[0].name, 'test_contextmanager_traceback')
117+
self.assertEqual(frames[0].line, 'raise RuntimeErrorSubclass(42)')
118+
119+
class StopIterationSubclass(StopIteration):
120+
pass
121+
122+
for stop_exc in (
123+
StopIteration('spam'),
124+
StopIterationSubclass('spam'),
125+
):
126+
with self.subTest(type=type(stop_exc)):
127+
try:
128+
with f():
129+
raise stop_exc
130+
except type(stop_exc) as e:
131+
self.assertIs(e, stop_exc)
132+
frames = traceback.extract_tb(e.__traceback__)
133+
else:
134+
self.fail(f'{stop_exc} was suppressed')
135+
136+
self.assertEqual(len(frames), 1)
137+
self.assertEqual(frames[0].name, 'test_contextmanager_traceback')
138+
self.assertEqual(frames[0].line, 'raise stop_exc')
139+
90140
def test_contextmanager_no_reraise(self):
91141
@contextmanager
92142
def whee():

Lib/test/test_contextlib_async.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
AsyncExitStack, nullcontext, aclosing, contextmanager)
55
import functools
66
from test import support
7+
import traceback
78
import unittest
89

910
from test.test_contextlib import TestBaseExitStack
@@ -124,6 +125,63 @@ async def woohoo():
124125
raise ZeroDivisionError()
125126
self.assertEqual(state, [1, 42, 999])
126127

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

0 commit comments

Comments
 (0)