From 9b47252d5433894c26fc07aa87b1bd6bb4238e5e Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 5 May 2022 10:47:59 -0700 Subject: [3.10] gh-92118: Add test for traceback when exception is modified by (Async)ExitStack.__exit__ (GH-92339) (GH-92343) --- Lib/test/test_contextlib.py | 37 +++++++++++++++++++++++++++++++++++++ Lib/test/test_contextlib_async.py | 7 +++++++ 2 files changed, 44 insertions(+) diff --git a/Lib/test/test_contextlib.py b/Lib/test/test_contextlib.py index fbaae2d..68bd45d 100644 --- a/Lib/test/test_contextlib.py +++ b/Lib/test/test_contextlib.py @@ -4,6 +4,7 @@ import io import sys import tempfile import threading +import traceback import unittest from contextlib import * # Tests __all__ from test import support @@ -701,6 +702,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 @@ -968,6 +1001,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 127d750..d6a34e2 100644 --- a/Lib/test/test_contextlib_async.py +++ b/Lib/test/test_contextlib_async.py @@ -412,6 +412,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() -- cgit v0.12