Skip to content

Commit 9b47252

Browse files
[3.10] gh-92118: Add test for traceback when exception is modified by (Async)ExitStack.__exit__ (GH-92339) (GH-92343)
1 parent bb2dcf1 commit 9b47252

File tree

2 files changed

+44
-0
lines changed

2 files changed

+44
-0
lines changed

Lib/test/test_contextlib.py

+37
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import sys
55
import tempfile
66
import threading
7+
import traceback
78
import unittest
89
from contextlib import * # Tests __all__
910
from test import support
@@ -701,6 +702,38 @@ def test_exit_suppress(self):
701702
stack.push(lambda *exc: True)
702703
1/0
703704

705+
def test_exit_exception_traceback(self):
706+
# This test captures the current behavior of ExitStack so that we know
707+
# if we ever unintendedly change it. It is not a statement of what the
708+
# desired behavior is (for instance, we may want to remove some of the
709+
# internal contextlib frames).
710+
711+
def raise_exc(exc):
712+
raise exc
713+
714+
try:
715+
with self.exit_stack() as stack:
716+
stack.callback(raise_exc, ValueError)
717+
1/0
718+
except ValueError as e:
719+
exc = e
720+
721+
self.assertIsInstance(exc, ValueError)
722+
ve_frames = traceback.extract_tb(exc.__traceback__)
723+
expected = \
724+
[('test_exit_exception_traceback', 'with self.exit_stack() as stack:')] + \
725+
self.callback_error_internal_frames + \
726+
[('_exit_wrapper', 'callback(*args, **kwds)'),
727+
('raise_exc', 'raise exc')]
728+
729+
self.assertEqual(
730+
[(f.name, f.line) for f in ve_frames], expected)
731+
732+
self.assertIsInstance(exc.__context__, ZeroDivisionError)
733+
zde_frames = traceback.extract_tb(exc.__context__.__traceback__)
734+
self.assertEqual([(f.name, f.line) for f in zde_frames],
735+
[('test_exit_exception_traceback', '1/0')])
736+
704737
def test_exit_exception_chaining_reference(self):
705738
# Sanity check to make sure that ExitStack chaining matches
706739
# actual nested with statements
@@ -968,6 +1001,10 @@ def first():
9681001

9691002
class TestExitStack(TestBaseExitStack, unittest.TestCase):
9701003
exit_stack = ExitStack
1004+
callback_error_internal_frames = [
1005+
('__exit__', 'raise exc_details[1]'),
1006+
('__exit__', 'if cb(*exc_details):'),
1007+
]
9711008

9721009

9731010
class TestRedirectStream:

Lib/test/test_contextlib_async.py

+7
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,13 @@ def __exit__(self, *exc_details):
412412
return self.run_coroutine(self.__aexit__(*exc_details))
413413

414414
exit_stack = SyncAsyncExitStack
415+
callback_error_internal_frames = [
416+
('__exit__', 'return self.run_coroutine(self.__aexit__(*exc_details))'),
417+
('run_coroutine', 'raise exc'),
418+
('run_coroutine', 'raise exc'),
419+
('__aexit__', 'raise exc_details[1]'),
420+
('__aexit__', 'cb_suppress = cb(*exc_details)'),
421+
]
415422

416423
def setUp(self):
417424
self.loop = asyncio.new_event_loop()

0 commit comments

Comments
 (0)