diff options
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/test/test_traceback.py | 61 | ||||
-rw-r--r-- | Lib/traceback.py | 29 |
2 files changed, 78 insertions, 12 deletions
diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index bffc03e663..8a3aa8a864 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -373,7 +373,7 @@ class TracebackFormatTests(unittest.TestCase): ' return g(count-1)\n' f' File "{__file__}", line {lineno_g+2}, in g\n' ' return g(count-1)\n' - ' [Previous line repeated 6 more times]\n' + ' [Previous line repeated 7 more times]\n' f' File "{__file__}", line {lineno_g+3}, in g\n' ' raise ValueError\n' 'ValueError\n' @@ -412,7 +412,7 @@ class TracebackFormatTests(unittest.TestCase): ' return h(count-1)\n' f' File "{__file__}", line {lineno_h+2}, in h\n' ' return h(count-1)\n' - ' [Previous line repeated 6 more times]\n' + ' [Previous line repeated 7 more times]\n' f' File "{__file__}", line {lineno_h+3}, in h\n' ' g()\n' ) @@ -420,6 +420,63 @@ class TracebackFormatTests(unittest.TestCase): actual = stderr_h.getvalue().splitlines() self.assertEqual(actual, expected) + # Check the boundary conditions. First, test just below the cutoff. + with captured_output("stderr") as stderr_g: + try: + g(traceback._RECURSIVE_CUTOFF) + except ValueError as exc: + render_exc() + else: + self.fail("no error raised") + result_g = ( + f' File "{__file__}", line {lineno_g+2}, in g\n' + ' return g(count-1)\n' + f' File "{__file__}", line {lineno_g+2}, in g\n' + ' return g(count-1)\n' + f' File "{__file__}", line {lineno_g+2}, in g\n' + ' return g(count-1)\n' + f' File "{__file__}", line {lineno_g+3}, in g\n' + ' raise ValueError\n' + 'ValueError\n' + ) + tb_line = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {lineno_g+71}, in _check_recursive_traceback_display\n' + ' g(traceback._RECURSIVE_CUTOFF)\n' + ) + expected = (tb_line + result_g).splitlines() + actual = stderr_g.getvalue().splitlines() + self.assertEqual(actual, expected) + + # Second, test just above the cutoff. + with captured_output("stderr") as stderr_g: + try: + g(traceback._RECURSIVE_CUTOFF + 1) + except ValueError as exc: + render_exc() + else: + self.fail("no error raised") + result_g = ( + f' File "{__file__}", line {lineno_g+2}, in g\n' + ' return g(count-1)\n' + f' File "{__file__}", line {lineno_g+2}, in g\n' + ' return g(count-1)\n' + f' File "{__file__}", line {lineno_g+2}, in g\n' + ' return g(count-1)\n' + ' [Previous line repeated 1 more time]\n' + f' File "{__file__}", line {lineno_g+3}, in g\n' + ' raise ValueError\n' + 'ValueError\n' + ) + tb_line = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {lineno_g+99}, in _check_recursive_traceback_display\n' + ' g(traceback._RECURSIVE_CUTOFF + 1)\n' + ) + expected = (tb_line + result_g).splitlines() + actual = stderr_g.getvalue().splitlines() + self.assertEqual(actual, expected) + def test_recursive_traceback_python(self): self._check_recursive_traceback_display(traceback.print_exc) diff --git a/Lib/traceback.py b/Lib/traceback.py index afab0a4b91..4e7605d15f 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -310,6 +310,8 @@ def walk_tb(tb): tb = tb.tb_next +_RECURSIVE_CUTOFF = 3 # Also hardcoded in traceback.c. + class StackSummary(list): """A stack of frames.""" @@ -398,18 +400,21 @@ class StackSummary(list): last_name = None count = 0 for frame in self: - if (last_file is not None and last_file == frame.filename and - last_line is not None and last_line == frame.lineno and - last_name is not None and last_name == frame.name): - count += 1 - else: - if count > 3: - result.append(f' [Previous line repeated {count-3} more times]\n') + if (last_file is None or last_file != frame.filename or + last_line is None or last_line != frame.lineno or + last_name is None or last_name != frame.name): + 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.filename last_line = frame.lineno last_name = frame.name count = 0 - if count >= 3: + count += 1 + if count > _RECURSIVE_CUTOFF: continue row = [] row.append(' File "{}", line {}, in {}\n'.format( @@ -420,8 +425,12 @@ class StackSummary(list): for name, value in sorted(frame.locals.items()): row.append(' {name} = {value}\n'.format(name=name, value=value)) result.append(''.join(row)) - if count > 3: - result.append(f' [Previous line repeated {count-3} more times]\n') + if count > _RECURSIVE_CUTOFF: + count -= _RECURSIVE_CUTOFF + result.append( + f' [Previous line repeated {count} more ' + f'time{"s" if count > 1 else ""}]\n' + ) return result |