summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIrit Katriel <1055913+iritkatriel@users.noreply.github.com>2022-05-05 12:39:33 -0400
committerGitHub <noreply@github.com>2022-05-05 17:39:33 +0100
commite65e587f9320947d73817fbe62c11d2a0fd50dfb (patch)
tree34a2266a5d5de238444a4c76a8967d2463e62113
parentae553b3561f0f8e1fed454bbbd55073e4ba711eb (diff)
downloadcpython-git-e65e587f9320947d73817fbe62c11d2a0fd50dfb.tar.gz
gh-92118: Add test for traceback when exception is modified by (Async)ExitStack.__exit__ (GH-92339)
-rw-r--r--Lib/test/test_contextlib.py36
-rw-r--r--Lib/test/test_contextlib_async.py7
2 files changed, 43 insertions, 0 deletions
diff --git a/Lib/test/test_contextlib.py b/Lib/test/test_contextlib.py
index bfe81173dd..31f5c74572 100644
--- a/Lib/test/test_contextlib.py
+++ b/Lib/test/test_contextlib.py
@@ -748,6 +748,38 @@ class TestBaseExitStack:
stack.push(lambda *exc: True)
1/0
+ def test_exit_exception_traceback(self):
+ # This test captures the current behavior of ExitStack so that we know
+ # if we ever unintendedly change it. It is not a statement of what the
+ # desired behavior is (for instance, we may want to remove some of the
+ # internal contextlib frames).
+
+ def raise_exc(exc):
+ raise exc
+
+ try:
+ with self.exit_stack() as stack:
+ stack.callback(raise_exc, ValueError)
+ 1/0
+ except ValueError as e:
+ exc = e
+
+ self.assertIsInstance(exc, ValueError)
+ ve_frames = traceback.extract_tb(exc.__traceback__)
+ expected = \
+ [('test_exit_exception_traceback', 'with self.exit_stack() as stack:')] + \
+ self.callback_error_internal_frames + \
+ [('_exit_wrapper', 'callback(*args, **kwds)'),
+ ('raise_exc', 'raise exc')]
+
+ self.assertEqual(
+ [(f.name, f.line) for f in ve_frames], expected)
+
+ self.assertIsInstance(exc.__context__, ZeroDivisionError)
+ zde_frames = traceback.extract_tb(exc.__context__.__traceback__)
+ self.assertEqual([(f.name, f.line) for f in zde_frames],
+ [('test_exit_exception_traceback', '1/0')])
+
def test_exit_exception_chaining_reference(self):
# Sanity check to make sure that ExitStack chaining matches
# actual nested with statements
@@ -1017,6 +1049,10 @@ class TestBaseExitStack:
class TestExitStack(TestBaseExitStack, unittest.TestCase):
exit_stack = ExitStack
+ callback_error_internal_frames = [
+ ('__exit__', 'raise exc_details[1]'),
+ ('__exit__', 'if cb(*exc_details):'),
+ ]
class TestRedirectStream:
diff --git a/Lib/test/test_contextlib_async.py b/Lib/test/test_contextlib_async.py
index 462e05cc79..76bd81c7d0 100644
--- a/Lib/test/test_contextlib_async.py
+++ b/Lib/test/test_contextlib_async.py
@@ -487,6 +487,13 @@ class TestAsyncExitStack(TestBaseExitStack, unittest.TestCase):
return self.run_coroutine(self.__aexit__(*exc_details))
exit_stack = SyncAsyncExitStack
+ callback_error_internal_frames = [
+ ('__exit__', 'return self.run_coroutine(self.__aexit__(*exc_details))'),
+ ('run_coroutine', 'raise exc'),
+ ('run_coroutine', 'raise exc'),
+ ('__aexit__', 'raise exc_details[1]'),
+ ('__aexit__', 'cb_suppress = cb(*exc_details)'),
+ ]
def setUp(self):
self.loop = asyncio.new_event_loop()