https://github.com/python/cpython/commit/e18dda96c967911fe387ed93c7efe105e7cc1274
commit: e18dda96c967911fe387ed93c7efe105e7cc1274
branch: main
author: yihong <[email protected]>
committer: picnixz <[email protected]>
date: 2025-09-28T11:49:18Z
summary:

gh-139374: colorize traceback when using `timeit` command-line interface 
(#139375)

---------

Signed-off-by: yihong0618 <[email protected]>
Co-authored-by: Stan Ulbrych <[email protected]>
Co-authored-by: Hugo van Kemenade <[email protected]>
Co-authored-by: Peter Bierma <[email protected]>
Co-authored-by: Bénédikt Tran <[email protected]>

files:
A Misc/NEWS.d/next/Library/2025-09-27-08-26-31.gh-issue-139374.hfh-dl.rst
M Doc/whatsnew/3.15.rst
M Lib/test/test_timeit.py
M Lib/timeit.py
M Lib/traceback.py

diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst
index 31e1bfd9a438ba..d8c5d9378008eb 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 <using-on-controlling-color>`.
+  (Contributed by Yi Hong in :gh:`139374`.)
+
+
 types
 ------
 
diff --git a/Lib/test/test_timeit.py b/Lib/test/test_timeit.py
index 2aeebea9f93d43..f8bc306b455a5d 100644
--- a/Lib/test/test_timeit.py
+++ b/Lib/test/test_timeit.py
@@ -4,8 +4,9 @@
 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 @@ def test_main_with_time_unit(self):
         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 e767f0187826df..80791acdeca23f 100644
--- a/Lib/timeit.py
+++ b/Lib/timeit.py
@@ -133,7 +133,7 @@ def __init__(self, stmt="pass", setup="pass", 
timer=default_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 @@ def print_exc(self, file=None):
 
         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 @@ def print_exc(self, file=None):
                                                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 callback(number, time_taken):
         try:
             number, _ = t.autorange(callback)
         except:
-            t.print_exc()
+            t.print_exc(colorize=colorize)
             return 1
 
         if verbose:
@@ -335,7 +344,7 @@ def callback(number, time_taken):
     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 8e2d8d72a0a32d..692d44837936ee 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 00000000000000..6504a1904102dd
--- /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.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to