From: yihong Date: Sun, 28 Sep 2025 11:49:18 +0000 (+0800) Subject: gh-139374: colorize traceback when using `timeit` command-line interface (#139375) X-Git-Tag: v3.15.0a1~192 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=e18dda96c967911fe387ed93c7efe105e7cc1274;p=thirdparty%2FPython%2Fcpython.git gh-139374: colorize traceback when using `timeit` command-line interface (#139375) --------- Signed-off-by: yihong0618 Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Co-authored-by: Peter Bierma Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 31e1bfd9a438..d8c5d9378008 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -536,6 +536,15 @@ tarfile (Contributed by Christoph Walcher in :gh:`57911`.) +timeit +------ + +* The command-line interface now colorizes error tracebacks + by default. This can be controlled with + :ref:`environment variables `. + (Contributed by Yi Hong in :gh:`139374`.) + + types ------ diff --git a/Lib/test/test_timeit.py b/Lib/test/test_timeit.py index 2aeebea9f93d..f8bc306b455a 100644 --- a/Lib/test/test_timeit.py +++ b/Lib/test/test_timeit.py @@ -4,8 +4,9 @@ import sys import io from textwrap import dedent -from test.support import captured_stdout -from test.support import captured_stderr +from test.support import ( + captured_stdout, captured_stderr, force_not_colorized, +) # timeit's default number of iterations. DEFAULT_NUMBER = 1000000 @@ -351,11 +352,13 @@ class TestTimeit(unittest.TestCase): self.assertEqual(error_stringio.getvalue(), "Unrecognized unit. Please select nsec, usec, msec, or sec.\n") + @force_not_colorized def test_main_exception(self): with captured_stderr() as error_stringio: s = self.run_main(switches=['1/0']) self.assert_exc_string(error_stringio.getvalue(), 'ZeroDivisionError') + @force_not_colorized def test_main_exception_fixed_reps(self): with captured_stderr() as error_stringio: s = self.run_main(switches=['-n1', '1/0']) diff --git a/Lib/timeit.py b/Lib/timeit.py index e767f0187826..80791acdeca2 100644 --- a/Lib/timeit.py +++ b/Lib/timeit.py @@ -133,7 +133,7 @@ class Timer: exec(code, global_ns, local_ns) self.inner = local_ns["inner"] - def print_exc(self, file=None): + def print_exc(self, file=None, **kwargs): """Helper to print a traceback from the timed code. Typical use: @@ -149,6 +149,11 @@ class Timer: The optional file argument directs where the traceback is sent; it defaults to sys.stderr. + + The optional colorize keyword argument controls whether the + traceback is colorized; it defaults to False for programmatic + usage. When used from the command line, this is automatically + set based on terminal capabilities. """ import linecache, traceback if self.src is not None: @@ -158,7 +163,8 @@ class Timer: dummy_src_name) # else the source is already stored somewhere else - traceback.print_exc(file=file) + kwargs['colorize'] = kwargs.get('colorize', False) + traceback.print_exc(file=file, **kwargs) def timeit(self, number=default_number): """Time 'number' executions of the main statement. @@ -257,9 +263,12 @@ def main(args=None, *, _wrap_timer=None): is not None, it must be a callable that accepts a timer function and returns another timer function (used for unit testing). """ + import getopt if args is None: args = sys.argv[1:] - import getopt + import _colorize + colorize = _colorize.can_colorize() + try: opts, args = getopt.getopt(args, "n:u:s:r:pvh", ["number=", "setup=", "repeat=", @@ -326,7 +335,7 @@ def main(args=None, *, _wrap_timer=None): try: number, _ = t.autorange(callback) except: - t.print_exc() + t.print_exc(colorize=colorize) return 1 if verbose: @@ -335,7 +344,7 @@ def main(args=None, *, _wrap_timer=None): try: raw_timings = t.repeat(repeat, number) except: - t.print_exc() + t.print_exc(colorize=colorize) return 1 def format_time(dt): diff --git a/Lib/traceback.py b/Lib/traceback.py index 8e2d8d72a0a3..692d44837936 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -206,9 +206,9 @@ def _safe_string(value, what, func=str): # -- -def print_exc(limit=None, file=None, chain=True): +def print_exc(limit=None, file=None, chain=True, **kwargs): """Shorthand for 'print_exception(sys.exception(), limit=limit, file=file, chain=chain)'.""" - print_exception(sys.exception(), limit=limit, file=file, chain=chain) + print_exception(sys.exception(), limit=limit, file=file, chain=chain, **kwargs) def format_exc(limit=None, chain=True): """Like print_exc() but return a string.""" diff --git a/Misc/NEWS.d/next/Library/2025-09-27-08-26-31.gh-issue-139374.hfh-dl.rst b/Misc/NEWS.d/next/Library/2025-09-27-08-26-31.gh-issue-139374.hfh-dl.rst new file mode 100644 index 000000000000..6504a1904102 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-09-27-08-26-31.gh-issue-139374.hfh-dl.rst @@ -0,0 +1 @@ +:mod:`timeit`: Add color to error tracebacks.