diff options
author | Irit Katriel <1055913+iritkatriel@users.noreply.github.com> | 2022-05-05 12:39:33 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-05-05 17:39:33 +0100 |
commit | e65e587f9320947d73817fbe62c11d2a0fd50dfb (patch) | |
tree | 34a2266a5d5de238444a4c76a8967d2463e62113 | |
parent | ae553b3561f0f8e1fed454bbbd55073e4ba711eb (diff) | |
download | cpython-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.py | 36 | ||||
-rw-r--r-- | Lib/test/test_contextlib_async.py | 7 |
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() |