From 21b27947b84548173b8f4a745de2e2e2e0493803 Mon Sep 17 00:00:00 2001 From: HarryLHW <123lhw321@gmail.com> Date: Tue, 11 Feb 2025 01:56:01 +0800 Subject: [PATCH 1/5] add a test that can pass in main --- Lib/test/test_traceback.py | 76 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 89980ae6f8573a..d11e00ed5638fe 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -2062,15 +2062,91 @@ def h(count=10): actual = stderr_g.getvalue().splitlines() self.assertEqual(actual, expected) + def _check_previous_line_repeated(self, render_exc): + self.maxDiff = None + + # See https://github.com/python/cpython/issues/128327 + def fib(number: int) -> int: + # wrong implementation + assert number > 0 + if number == 1: + return 1 + return fib(number - 1) + fib(number - 2) + + lineno_fib = fib.__code__.co_firstlineno + + with captured_output("stderr") as stderr_fib4: + try: + fib(traceback._RECURSIVE_CUTOFF + 1) + except AssertionError: + render_exc() + else: + self.fail("no error raised") + result_fib4 = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {lineno_fib + 11}, in _check_previous_line_repeated\n' + ' fib(traceback._RECURSIVE_CUTOFF + 1)\n' + ' ~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' + f' File "{__file__}", line {lineno_fib + 5}, in fib\n' + ' return fib(number - 1) + fib(number - 2)\n' + ' ~~~^^^^^^^^^^^^\n' + f' File "{__file__}", line {lineno_fib + 5}, in fib\n' + ' return fib(number - 1) + fib(number - 2)\n' + ' ~~~^^^^^^^^^^^^\n' + f' File "{__file__}", line {lineno_fib + 5}, in fib\n' + ' return fib(number - 1) + fib(number - 2)\n' + ' ~~~^^^^^^^^^^^^\n' + f' File "{__file__}", line {lineno_fib + 2}, in fib\n' + ' assert number > 0\n' + ' ^^^^^^^^^^\n' + 'AssertionError\n' + ) + expected = self._maybe_filter_debug_ranges((result_fib4).splitlines()) + actual = stderr_fib4.getvalue().splitlines() + self.assertEqual(actual, expected) + + with captured_output("stderr") as stderr_fib5: + try: + fib(traceback._RECURSIVE_CUTOFF + 2) + except AssertionError: + render_exc() + else: + self.fail("no error raised") + result_fib5 = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {lineno_fib + 41}, in _check_previous_line_repeated\n' + ' fib(traceback._RECURSIVE_CUTOFF + 2)\n' + ' ~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' + f' File "{__file__}", line {lineno_fib + 5}, in fib\n' + ' return fib(number - 1) + fib(number - 2)\n' + ' ~~~^^^^^^^^^^^^\n' + f' File "{__file__}", line {lineno_fib + 5}, in fib\n' + ' return fib(number - 1) + fib(number - 2)\n' + ' ~~~^^^^^^^^^^^^\n' + f' File "{__file__}", line {lineno_fib + 5}, in fib\n' + ' return fib(number - 1) + fib(number - 2)\n' + ' ~~~^^^^^^^^^^^^\n' + ' [Previous line repeated 1 more time]\n' + f' File "{__file__}", line {lineno_fib + 2}, in fib\n' + ' assert number > 0\n' + ' ^^^^^^^^^^\n' + 'AssertionError\n' + ) + expected = self._maybe_filter_debug_ranges((result_fib5).splitlines()) + actual = stderr_fib5.getvalue().splitlines() + self.assertEqual(actual, expected) + @requires_debug_ranges() def test_recursive_traceback(self): if self.DEBUG_RANGES: self._check_recursive_traceback_display(traceback.print_exc) + self._check_previous_line_repeated(traceback.print_exc) else: from _testcapi import exception_print def render_exc(): exception_print(sys.exception()) self._check_recursive_traceback_display(render_exc) + self._check_previous_line_repeated(render_exc) def test_format_stack(self): def fmt(): From 441d595ba6a94f2aba57cd1b5504bf7ac94bdff3 Mon Sep 17 00:00:00 2001 From: HarryLHW <123lhw321@gmail.com> Date: Tue, 11 Feb 2025 01:56:17 +0800 Subject: [PATCH 2/5] fix bug --- Lib/traceback.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/Lib/traceback.py b/Lib/traceback.py index 31c73efcef5a52..db406cf72e9090 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -741,26 +741,20 @@ def format(self, **kwargs): """ colorize = kwargs.get("colorize", False) result = [] - last_file = None - last_line = None - last_name = None + last_formatted_frame = None count = 0 for frame_summary in self: formatted_frame = self.format_frame_summary(frame_summary, colorize=colorize) if formatted_frame is None: continue - if (last_file is None or last_file != frame_summary.filename or - last_line is None or last_line != frame_summary.lineno or - last_name is None or last_name != frame_summary.name): + if last_formatted_frame is None or last_formatted_frame != formatted_frame: if count > _RECURSIVE_CUTOFF: count -= _RECURSIVE_CUTOFF result.append( f' [Previous line repeated {count} more ' f'time{"s" if count > 1 else ""}]\n' ) - last_file = frame_summary.filename - last_line = frame_summary.lineno - last_name = frame_summary.name + last_formatted_frame = formatted_frame count = 0 count += 1 if count > _RECURSIVE_CUTOFF: From 306e01d9db125ad5e56541ac1f1c93ee51ed88e6 Mon Sep 17 00:00:00 2001 From: HarryLHW <123lhw321@gmail.com> Date: Tue, 11 Feb 2025 02:56:08 +0800 Subject: [PATCH 3/5] test fails, fix test to pass --- Lib/test/test_traceback.py | 50 +++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index d11e00ed5638fe..0a7921cab4ff4c 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -2063,8 +2063,6 @@ def h(count=10): self.assertEqual(actual, expected) def _check_previous_line_repeated(self, render_exc): - self.maxDiff = None - # See https://github.com/python/cpython/issues/128327 def fib(number: int) -> int: # wrong implementation @@ -2075,18 +2073,18 @@ def fib(number: int) -> int: lineno_fib = fib.__code__.co_firstlineno - with captured_output("stderr") as stderr_fib4: + with captured_output("stderr") as stderr_fib5: try: - fib(traceback._RECURSIVE_CUTOFF + 1) + fib(5) except AssertionError: render_exc() else: self.fail("no error raised") - result_fib4 = ( + result_fib5 = ( 'Traceback (most recent call last):\n' f' File "{__file__}", line {lineno_fib + 11}, in _check_previous_line_repeated\n' - ' fib(traceback._RECURSIVE_CUTOFF + 1)\n' - ' ~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' + ' fib(5)\n' + ' ~~~^^^\n' f' File "{__file__}", line {lineno_fib + 5}, in fib\n' ' return fib(number - 1) + fib(number - 2)\n' ' ~~~^^^^^^^^^^^^\n' @@ -2095,28 +2093,35 @@ def fib(number: int) -> int: ' ~~~^^^^^^^^^^^^\n' f' File "{__file__}", line {lineno_fib + 5}, in fib\n' ' return fib(number - 1) + fib(number - 2)\n' - ' ~~~^^^^^^^^^^^^\n' + ' ~~~^^^^^^^^^^^^\n' + f'{ + (f' File "{__file__}", line {lineno_fib + 5}, in fib\n' + ' return fib(number - 1) + fib(number - 2)\n' + ' ~~~^^^^^^^^^^^^\n') + if self.DEBUG_RANGES else + ' [Previous line repeated 1 more time]\n' + }' f' File "{__file__}", line {lineno_fib + 2}, in fib\n' ' assert number > 0\n' ' ^^^^^^^^^^\n' 'AssertionError\n' ) - expected = self._maybe_filter_debug_ranges((result_fib4).splitlines()) - actual = stderr_fib4.getvalue().splitlines() + expected = self._maybe_filter_debug_ranges((result_fib5).splitlines()) + actual = stderr_fib5.getvalue().splitlines() self.assertEqual(actual, expected) - with captured_output("stderr") as stderr_fib5: + with captured_output("stderr") as stderr_fib6: try: - fib(traceback._RECURSIVE_CUTOFF + 2) + fib(6) except AssertionError: render_exc() else: self.fail("no error raised") - result_fib5 = ( + result_fib6 = ( 'Traceback (most recent call last):\n' - f' File "{__file__}", line {lineno_fib + 41}, in _check_previous_line_repeated\n' - ' fib(traceback._RECURSIVE_CUTOFF + 2)\n' - ' ~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' + f' File "{__file__}", line {lineno_fib + 48}, in _check_previous_line_repeated\n' + ' fib(6)\n' + ' ~~~^^^\n' f' File "{__file__}", line {lineno_fib + 5}, in fib\n' ' return fib(number - 1) + fib(number - 2)\n' ' ~~~^^^^^^^^^^^^\n' @@ -2126,14 +2131,21 @@ def fib(number: int) -> int: f' File "{__file__}", line {lineno_fib + 5}, in fib\n' ' return fib(number - 1) + fib(number - 2)\n' ' ~~~^^^^^^^^^^^^\n' - ' [Previous line repeated 1 more time]\n' + f'{ + (' [Previous line repeated 1 more time]\n' + f' File "{__file__}", line {lineno_fib + 5}, in fib\n' + ' return fib(number - 1) + fib(number - 2)\n' + ' ~~~^^^^^^^^^^^^\n') + if self.DEBUG_RANGES else + ' [Previous line repeated 2 more times]\n' + }' f' File "{__file__}", line {lineno_fib + 2}, in fib\n' ' assert number > 0\n' ' ^^^^^^^^^^\n' 'AssertionError\n' ) - expected = self._maybe_filter_debug_ranges((result_fib5).splitlines()) - actual = stderr_fib5.getvalue().splitlines() + expected = self._maybe_filter_debug_ranges((result_fib6).splitlines()) + actual = stderr_fib6.getvalue().splitlines() self.assertEqual(actual, expected) @requires_debug_ranges() From 08c039597442e06cbb270631258bfc3e9128fc4b Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 19:52:57 +0000 Subject: [PATCH 4/5] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2025-02-10-19-52-55.gh-issue-128327.Lx73HD.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-02-10-19-52-55.gh-issue-128327.Lx73HD.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-02-10-19-52-55.gh-issue-128327.Lx73HD.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-10-19-52-55.gh-issue-128327.Lx73HD.rst new file mode 100644 index 00000000000000..32e359e539ffa3 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-10-19-52-55.gh-issue-128327.Lx73HD.rst @@ -0,0 +1 @@ +Improve the condition of showing [Previous line repeated %d more time(s)]. Now repeated frames must have the same error locations (indicated by caret (^) and/or tilde (~) characters). From ccc33bed613e3e15509181b9238765fa2856e613 Mon Sep 17 00:00:00 2001 From: HarryLHW <123lhw321@gmail.com> Date: Tue, 11 Feb 2025 22:12:34 +0800 Subject: [PATCH 5/5] improve test readability --- Lib/test/test_traceback.py | 108 ++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 55 deletions(-) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 0a7921cab4ff4c..ba749f61d51bab 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -2080,33 +2080,32 @@ def fib(number: int) -> int: render_exc() else: self.fail("no error raised") - result_fib5 = ( - 'Traceback (most recent call last):\n' - f' File "{__file__}", line {lineno_fib + 11}, in _check_previous_line_repeated\n' - ' fib(5)\n' - ' ~~~^^^\n' - f' File "{__file__}", line {lineno_fib + 5}, in fib\n' - ' return fib(number - 1) + fib(number - 2)\n' - ' ~~~^^^^^^^^^^^^\n' - f' File "{__file__}", line {lineno_fib + 5}, in fib\n' - ' return fib(number - 1) + fib(number - 2)\n' - ' ~~~^^^^^^^^^^^^\n' - f' File "{__file__}", line {lineno_fib + 5}, in fib\n' - ' return fib(number - 1) + fib(number - 2)\n' - ' ~~~^^^^^^^^^^^^\n' - f'{ - (f' File "{__file__}", line {lineno_fib + 5}, in fib\n' - ' return fib(number - 1) + fib(number - 2)\n' - ' ~~~^^^^^^^^^^^^\n') - if self.DEBUG_RANGES else - ' [Previous line repeated 1 more time]\n' - }' - f' File "{__file__}", line {lineno_fib + 2}, in fib\n' - ' assert number > 0\n' - ' ^^^^^^^^^^\n' - 'AssertionError\n' - ) - expected = self._maybe_filter_debug_ranges((result_fib5).splitlines()) + result_fib5 = [ + 'Traceback (most recent call last):', + f' File "{__file__}", line {lineno_fib + 11}, in _check_previous_line_repeated', + ' fib(5)', + ' ~~~^^^', + f' File "{__file__}", line {lineno_fib + 5}, in fib', + ' return fib(number - 1) + fib(number - 2)', + ' ~~~^^^^^^^^^^^^', + f' File "{__file__}", line {lineno_fib + 5}, in fib', + ' return fib(number - 1) + fib(number - 2)', + ' ~~~^^^^^^^^^^^^', + f' File "{__file__}", line {lineno_fib + 5}, in fib', + ' return fib(number - 1) + fib(number - 2)', + ' ~~~^^^^^^^^^^^^', + ] + if self.DEBUG_RANGES: + result_fib5.append(f' File "{__file__}", line {lineno_fib + 5}, in fib') + result_fib5.append(' return fib(number - 1) + fib(number - 2)') + result_fib5.append(' ~~~^^^^^^^^^^^^') + else: + result_fib5.append(' [Previous line repeated 1 more time]') + result_fib5.append(f' File "{__file__}", line {lineno_fib + 2}, in fib') + result_fib5.append(' assert number > 0') + result_fib5.append(' ^^^^^^^^^^') + result_fib5.append('AssertionError') + expected = self._maybe_filter_debug_ranges(result_fib5) actual = stderr_fib5.getvalue().splitlines() self.assertEqual(actual, expected) @@ -2117,34 +2116,33 @@ def fib(number: int) -> int: render_exc() else: self.fail("no error raised") - result_fib6 = ( - 'Traceback (most recent call last):\n' - f' File "{__file__}", line {lineno_fib + 48}, in _check_previous_line_repeated\n' - ' fib(6)\n' - ' ~~~^^^\n' - f' File "{__file__}", line {lineno_fib + 5}, in fib\n' - ' return fib(number - 1) + fib(number - 2)\n' - ' ~~~^^^^^^^^^^^^\n' - f' File "{__file__}", line {lineno_fib + 5}, in fib\n' - ' return fib(number - 1) + fib(number - 2)\n' - ' ~~~^^^^^^^^^^^^\n' - f' File "{__file__}", line {lineno_fib + 5}, in fib\n' - ' return fib(number - 1) + fib(number - 2)\n' - ' ~~~^^^^^^^^^^^^\n' - f'{ - (' [Previous line repeated 1 more time]\n' - f' File "{__file__}", line {lineno_fib + 5}, in fib\n' - ' return fib(number - 1) + fib(number - 2)\n' - ' ~~~^^^^^^^^^^^^\n') - if self.DEBUG_RANGES else - ' [Previous line repeated 2 more times]\n' - }' - f' File "{__file__}", line {lineno_fib + 2}, in fib\n' - ' assert number > 0\n' - ' ^^^^^^^^^^\n' - 'AssertionError\n' - ) - expected = self._maybe_filter_debug_ranges((result_fib6).splitlines()) + result_fib6 = [ + 'Traceback (most recent call last):', + f' File "{__file__}", line {lineno_fib + 47}, in _check_previous_line_repeated', + ' fib(6)', + ' ~~~^^^', + f' File "{__file__}", line {lineno_fib + 5}, in fib', + ' return fib(number - 1) + fib(number - 2)', + ' ~~~^^^^^^^^^^^^', + f' File "{__file__}", line {lineno_fib + 5}, in fib', + ' return fib(number - 1) + fib(number - 2)', + ' ~~~^^^^^^^^^^^^', + f' File "{__file__}", line {lineno_fib + 5}, in fib', + ' return fib(number - 1) + fib(number - 2)', + ' ~~~^^^^^^^^^^^^', + ] + if self.DEBUG_RANGES: + result_fib6.append(' [Previous line repeated 1 more time]') + result_fib6.append(f' File "{__file__}", line {lineno_fib + 5}, in fib') + result_fib6.append(' return fib(number - 1) + fib(number - 2)') + result_fib6.append(' ~~~^^^^^^^^^^^^') + else: + result_fib6.append(' [Previous line repeated 2 more times]') + result_fib6.append(f' File "{__file__}", line {lineno_fib + 2}, in fib') + result_fib6.append(' assert number > 0') + result_fib6.append(' ^^^^^^^^^^') + result_fib6.append('AssertionError') + expected = self._maybe_filter_debug_ranges(result_fib6) actual = stderr_fib6.getvalue().splitlines() self.assertEqual(actual, expected)