https://github.com/python/cpython/commit/4ac916ae33b962cb6b4f8849556403594b22a7f2
commit: 4ac916ae33b962cb6b4f8849556403594b22a7f2
branch: main
author: Hugo van Kemenade <1324225+hug...@users.noreply.github.com>
committer: ambv <luk...@langa.pl>
date: 2025-05-05T19:46:46+02:00
summary:

gh-130645: Add color to stdlib argparse CLIs (gh-133380)

files:
A Misc/NEWS.d/next/Library/2025-05-04-16-00-01.gh-issue-130645.yNwKue.rst
M Lib/ast.py
M Lib/calendar.py
M Lib/code.py
M Lib/compileall.py
M Lib/dis.py
M Lib/doctest.py
M Lib/ensurepip/__init__.py
M Lib/gzip.py
M Lib/http/server.py
M Lib/inspect.py
M Lib/json/tool.py
M Lib/mimetypes.py
M Lib/pdb.py
M Lib/pickle.py
M Lib/pickletools.py
M Lib/platform.py
M Lib/py_compile.py
M Lib/random.py
M Lib/sqlite3/__main__.py
M Lib/tarfile.py
M Lib/test/test_ast/test_ast.py
M Lib/test/test_calendar.py
M Lib/test/test_mimetypes.py
M Lib/test/test_pickle.py
M Lib/test/test_platform.py
M Lib/test/test_random.py
M Lib/test/test_sqlite3/test_cli.py
M Lib/tokenize.py
M Lib/trace.py
M Lib/unittest/main.py
M Lib/uuid.py
M Lib/venv/__init__.py
M Lib/webbrowser.py
M Lib/zipapp.py
M Lib/zipfile/__init__.py

diff --git a/Lib/ast.py b/Lib/ast.py
index be6ed0805d63dd..b9791bf52d3e08 100644
--- a/Lib/ast.py
+++ b/Lib/ast.py
@@ -630,7 +630,7 @@ def main(args=None):
     import argparse
     import sys
 
-    parser = argparse.ArgumentParser()
+    parser = argparse.ArgumentParser(color=True)
     parser.add_argument('infile', nargs='?', default='-',
                         help='the file to parse; defaults to stdin')
     parser.add_argument('-m', '--mode', default='exec',
diff --git a/Lib/calendar.py b/Lib/calendar.py
index 01a76ff8e78c45..18f76d52ff8581 100644
--- a/Lib/calendar.py
+++ b/Lib/calendar.py
@@ -810,7 +810,7 @@ def timegm(tuple):
 
 def main(args=None):
     import argparse
-    parser = argparse.ArgumentParser()
+    parser = argparse.ArgumentParser(color=True)
     textgroup = parser.add_argument_group('text only arguments')
     htmlgroup = parser.add_argument_group('html only arguments')
     textgroup.add_argument(
diff --git a/Lib/code.py b/Lib/code.py
index 41331dfd071f11..b134886dc267fb 100644
--- a/Lib/code.py
+++ b/Lib/code.py
@@ -385,7 +385,7 @@ def interact(banner=None, readfunc=None, local=None, 
exitmsg=None, local_exit=Fa
 if __name__ == "__main__":
     import argparse
 
-    parser = argparse.ArgumentParser()
+    parser = argparse.ArgumentParser(color=True)
     parser.add_argument('-q', action='store_true',
                        help="don't print version and copyright messages")
     args = parser.parse_args()
diff --git a/Lib/compileall.py b/Lib/compileall.py
index 47e2446356e7d7..67fe370451e1ef 100644
--- a/Lib/compileall.py
+++ b/Lib/compileall.py
@@ -317,7 +317,9 @@ def main():
     import argparse
 
     parser = argparse.ArgumentParser(
-        description='Utilities to support installing Python libraries.')
+        description='Utilities to support installing Python libraries.',
+        color=True,
+    )
     parser.add_argument('-l', action='store_const', const=0,
                         default=None, dest='maxlevels',
                         help="don't recurse into subdirectories")
diff --git a/Lib/dis.py b/Lib/dis.py
index cb6d077a391677..d6d2c1386dd785 100644
--- a/Lib/dis.py
+++ b/Lib/dis.py
@@ -1131,7 +1131,7 @@ def dis(self):
 def main(args=None):
     import argparse
 
-    parser = argparse.ArgumentParser()
+    parser = argparse.ArgumentParser(color=True)
     parser.add_argument('-C', '--show-caches', action='store_true',
                         help='show inline caches')
     parser.add_argument('-O', '--show-offsets', action='store_true',
diff --git a/Lib/doctest.py b/Lib/doctest.py
index e02e73ed722f7e..2acb6cb79f394d 100644
--- a/Lib/doctest.py
+++ b/Lib/doctest.py
@@ -2870,7 +2870,7 @@ def get(self):
 def _test():
     import argparse
 
-    parser = argparse.ArgumentParser(description="doctest runner")
+    parser = argparse.ArgumentParser(description="doctest runner", color=True)
     parser.add_argument('-v', '--verbose', action='store_true', default=False,
                         help='print very verbose output for all tests')
     parser.add_argument('-o', '--option', action='append',
diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py
index 6fc9f39b24cb9c..aa641e94a8b336 100644
--- a/Lib/ensurepip/__init__.py
+++ b/Lib/ensurepip/__init__.py
@@ -205,7 +205,7 @@ def _uninstall_helper(*, verbosity=0):
 
 def _main(argv=None):
     import argparse
-    parser = argparse.ArgumentParser()
+    parser = argparse.ArgumentParser(color=True)
     parser.add_argument(
         "--version",
         action="version",
diff --git a/Lib/gzip.py b/Lib/gzip.py
index b7375b2547314f..c00f51858de0f0 100644
--- a/Lib/gzip.py
+++ b/Lib/gzip.py
@@ -667,7 +667,9 @@ def main():
     from argparse import ArgumentParser
     parser = ArgumentParser(description=
         "A simple command line interface for the gzip module: act like gzip, "
-        "but do not delete the input file.")
+        "but do not delete the input file.",
+        color=True,
+    )
     group = parser.add_mutually_exclusive_group()
     group.add_argument('--fast', action='store_true', help='compress faster')
     group.add_argument('--best', action='store_true', help='compress better')
diff --git a/Lib/http/server.py b/Lib/http/server.py
index a2aad4c9be3c51..64f766f9bc2c1b 100644
--- a/Lib/http/server.py
+++ b/Lib/http/server.py
@@ -1340,7 +1340,7 @@ def test(HandlerClass=BaseHTTPRequestHandler,
     import argparse
     import contextlib
 
-    parser = argparse.ArgumentParser()
+    parser = argparse.ArgumentParser(color=True)
     parser.add_argument('--cgi', action='store_true',
                         help='run as CGI server')
     parser.add_argument('-b', '--bind', metavar='ADDRESS',
diff --git a/Lib/inspect.py b/Lib/inspect.py
index 9592559ba6dcaa..52c9bb05b31f37 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -3343,7 +3343,7 @@ def _main():
     import argparse
     import importlib
 
-    parser = argparse.ArgumentParser()
+    parser = argparse.ArgumentParser(color=True)
     parser.add_argument(
         'object',
          help="The object to be analysed. "
diff --git a/Lib/json/tool.py b/Lib/json/tool.py
index 585583da8604ac..de186368545329 100644
--- a/Lib/json/tool.py
+++ b/Lib/json/tool.py
@@ -44,7 +44,7 @@ def _colorize_json(json_str):
 def main():
     description = ('A simple command line interface for json module '
                    'to validate and pretty-print JSON objects.')
-    parser = argparse.ArgumentParser(description=description)
+    parser = argparse.ArgumentParser(description=description, color=True)
     parser.add_argument('infile', nargs='?',
                         help='a JSON file to be validated or pretty-printed',
                         default='-')
diff --git a/Lib/mimetypes.py b/Lib/mimetypes.py
index b5a1b8da2638e0..33e86d51a0fe50 100644
--- a/Lib/mimetypes.py
+++ b/Lib/mimetypes.py
@@ -698,7 +698,9 @@ def _default_mime_types():
 def _parse_args(args):
     from argparse import ArgumentParser
 
-    parser = ArgumentParser(description='map filename extensions to MIME 
types')
+    parser = ArgumentParser(
+        description='map filename extensions to MIME types', color=True
+    )
     parser.add_argument(
         '-e', '--extension',
         action='store_true',
diff --git a/Lib/pdb.py b/Lib/pdb.py
index 195bfa557ef1e4..b30df59d793f24 100644
--- a/Lib/pdb.py
+++ b/Lib/pdb.py
@@ -3296,10 +3296,13 @@ def help():
 def main():
     import argparse
 
-    parser = argparse.ArgumentParser(usage="%(prog)s [-h] [-c command] (-m 
module | -p pid | pyfile) [args ...]",
-                                     description=_usage,
-                                     
formatter_class=argparse.RawDescriptionHelpFormatter,
-                                     allow_abbrev=False)
+    parser = argparse.ArgumentParser(
+        usage="%(prog)s [-h] [-c command] (-m module | -p pid | pyfile) [args 
...]",
+        description=_usage,
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+        allow_abbrev=False,
+        color=True,
+    )
 
     # We need to maunally get the script from args, because the first 
positional
     # arguments could be either the script we need to debug, or the argument
diff --git a/Lib/pickle.py b/Lib/pickle.py
index 4fa3632d1a738f..beaefae0479d3c 100644
--- a/Lib/pickle.py
+++ b/Lib/pickle.py
@@ -1911,7 +1911,9 @@ def _main(args=None):
     import argparse
     import pprint
     parser = argparse.ArgumentParser(
-        description='display contents of the pickle files')
+        description='display contents of the pickle files',
+        color=True,
+    )
     parser.add_argument(
         'pickle_file',
         nargs='+', help='the pickle file')
diff --git a/Lib/pickletools.py b/Lib/pickletools.py
index 53f25ea4e46b4d..bcddfb722bd26d 100644
--- a/Lib/pickletools.py
+++ b/Lib/pickletools.py
@@ -2842,7 +2842,9 @@ def __init__(self, value):
 if __name__ == "__main__":
     import argparse
     parser = argparse.ArgumentParser(
-        description='disassemble one or more pickle files')
+        description='disassemble one or more pickle files',
+        color=True,
+    )
     parser.add_argument(
         'pickle_file',
         nargs='+', help='the pickle file')
diff --git a/Lib/platform.py b/Lib/platform.py
index 507552f360ba59..55e211212d4209 100644
--- a/Lib/platform.py
+++ b/Lib/platform.py
@@ -1467,7 +1467,7 @@ def invalidate_caches():
 def _parse_args(args: list[str] | None):
     import argparse
 
-    parser = argparse.ArgumentParser()
+    parser = argparse.ArgumentParser(color=True)
     parser.add_argument("args", nargs="*", choices=["nonaliased", "terse"])
     parser.add_argument(
         "--terse",
diff --git a/Lib/py_compile.py b/Lib/py_compile.py
index 388614e51b1847..43d8ec90ffb6b1 100644
--- a/Lib/py_compile.py
+++ b/Lib/py_compile.py
@@ -177,7 +177,7 @@ def main():
     import argparse
 
     description = 'A simple command-line interface for py_compile module.'
-    parser = argparse.ArgumentParser(description=description)
+    parser = argparse.ArgumentParser(description=description, color=True)
     parser.add_argument(
         '-q', '--quiet',
         action='store_true',
diff --git a/Lib/random.py b/Lib/random.py
index 5e5d0c4c694a1c..86d562f0b8aaf6 100644
--- a/Lib/random.py
+++ b/Lib/random.py
@@ -1011,7 +1011,7 @@ def _test(N=10_000):
 def _parse_args(arg_list: list[str] | None):
     import argparse
     parser = argparse.ArgumentParser(
-        formatter_class=argparse.RawTextHelpFormatter)
+        formatter_class=argparse.RawTextHelpFormatter, color=True)
     group = parser.add_mutually_exclusive_group()
     group.add_argument(
         "-c", "--choice", nargs="+",
diff --git a/Lib/sqlite3/__main__.py b/Lib/sqlite3/__main__.py
index 79a6209468dae4..002f1986cddb89 100644
--- a/Lib/sqlite3/__main__.py
+++ b/Lib/sqlite3/__main__.py
@@ -65,6 +65,7 @@ def runsource(self, source, filename="<input>", 
symbol="single"):
 def main(*args):
     parser = ArgumentParser(
         description="Python sqlite3 CLI",
+        color=True,
     )
     parser.add_argument(
         "filename", type=str, default=":memory:", nargs="?",
diff --git a/Lib/tarfile.py b/Lib/tarfile.py
index 82c5f6704cbd24..28581f3e7a2692 100644
--- a/Lib/tarfile.py
+++ b/Lib/tarfile.py
@@ -2883,7 +2883,7 @@ def main():
     import argparse
 
     description = 'A simple command-line interface for tarfile module.'
-    parser = argparse.ArgumentParser(description=description)
+    parser = argparse.ArgumentParser(description=description, color=True)
     parser.add_argument('-v', '--verbose', action='store_true', default=False,
                         help='Verbose output')
     parser.add_argument('--filter', metavar='<filtername>',
diff --git a/Lib/test/test_ast/test_ast.py b/Lib/test/test_ast/test_ast.py
index ae82395e9a005a..530b5ec428eaa8 100644
--- a/Lib/test/test_ast/test_ast.py
+++ b/Lib/test/test_ast/test_ast.py
@@ -3289,6 +3289,7 @@ def f(x: int) -> int:
                     with self.subTest(flags=args):
                         self.invoke_ast(*args)
 
+    @support.force_not_colorized
     def test_help_message(self):
         for flag in ('-h', '--help', '--unknown'):
             with self.subTest(flag=flag):
diff --git a/Lib/test/test_calendar.py b/Lib/test/test_calendar.py
index 073df310bb49eb..cbfee604b7a8b5 100644
--- a/Lib/test/test_calendar.py
+++ b/Lib/test/test_calendar.py
@@ -987,6 +987,7 @@ def assertFailure(self, *args):
         self.assertCLIFails(*args)
         self.assertCmdFails(*args)
 
+    @support.force_not_colorized
     def test_help(self):
         stdout = self.run_cmd_ok('-h')
         self.assertIn(b'usage:', stdout)
diff --git a/Lib/test/test_mimetypes.py b/Lib/test/test_mimetypes.py
index dad5dbde7cd783..1db3277e390275 100644
--- a/Lib/test/test_mimetypes.py
+++ b/Lib/test/test_mimetypes.py
@@ -6,7 +6,7 @@
 import unittest.mock
 from platform import win32_edition
 from test import support
-from test.support import os_helper
+from test.support import force_not_colorized, os_helper
 
 try:
     import _winapi
@@ -437,6 +437,7 @@ def test__all__(self):
 
 
 class CommandLineTest(unittest.TestCase):
+    @force_not_colorized
     def test_parse_args(self):
         args, help_text = mimetypes._parse_args("-h")
         self.assertTrue(help_text.startswith("usage: "))
diff --git a/Lib/test/test_pickle.py b/Lib/test/test_pickle.py
index 296d4b882e138c..3b46e524aac477 100644
--- a/Lib/test/test_pickle.py
+++ b/Lib/test/test_pickle.py
@@ -745,6 +745,7 @@ def test_invocation(self):
             expect = self.text_normalize(expect)
             self.assertListEqual(res.splitlines(), expect.splitlines())
 
+    @support.force_not_colorized
     def test_unknown_flag(self):
         stderr = io.StringIO()
         with self.assertRaises(SystemExit):
diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py
index b90edc05e0454e..818e807dd3a6fb 100644
--- a/Lib/test/test_platform.py
+++ b/Lib/test/test_platform.py
@@ -794,6 +794,7 @@ def test_arg_parsing(self):
                     self.invoke_platform(*flags)
                     obj.assert_called_once_with(aliased, terse)
 
+    @support.force_not_colorized
     def test_help(self):
         output = io.StringIO()
 
diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py
index 96f6cc86219a5d..43957f525f10c0 100644
--- a/Lib/test/test_random.py
+++ b/Lib/test/test_random.py
@@ -1411,6 +1411,7 @@ def test_after_fork(self):
 
 
 class CommandLineTest(unittest.TestCase):
+    @support.force_not_colorized
     def test_parse_args(self):
         args, help_text = random._parse_args(shlex.split("--choice a b c"))
         self.assertEqual(args.choice, ["a", "b", "c"])
diff --git a/Lib/test/test_sqlite3/test_cli.py 
b/Lib/test/test_sqlite3/test_cli.py
index dcd90d11d46819..ad0dcb3cccb5da 100644
--- a/Lib/test/test_sqlite3/test_cli.py
+++ b/Lib/test/test_sqlite3/test_cli.py
@@ -4,7 +4,12 @@
 
 from sqlite3.__main__ import main as cli
 from test.support.os_helper import TESTFN, unlink
-from test.support import captured_stdout, captured_stderr, captured_stdin
+from test.support import (
+    captured_stdout,
+    captured_stderr,
+    captured_stdin,
+    force_not_colorized,
+)
 
 
 class CommandLineInterface(unittest.TestCase):
@@ -32,6 +37,7 @@ def expect_failure(self, *args):
         self.assertEqual(out, "")
         return err
 
+    @force_not_colorized
     def test_cli_help(self):
         out = self.expect_success("-h")
         self.assertIn("usage: ", out)
diff --git a/Lib/tokenize.py b/Lib/tokenize.py
index 117b485b9344e6..8d01fd7bce41b0 100644
--- a/Lib/tokenize.py
+++ b/Lib/tokenize.py
@@ -518,7 +518,7 @@ def error(message, filename=None, location=None):
         sys.exit(1)
 
     # Parse the arguments and options
-    parser = argparse.ArgumentParser()
+    parser = argparse.ArgumentParser(color=True)
     parser.add_argument(dest='filename', nargs='?',
                         metavar='filename.py',
                         help='the file to tokenize; defaults to stdin')
diff --git a/Lib/trace.py b/Lib/trace.py
index a87bc6d61a884f..cf8817f4383fc1 100644
--- a/Lib/trace.py
+++ b/Lib/trace.py
@@ -604,7 +604,7 @@ def results(self):
 def main():
     import argparse
 
-    parser = argparse.ArgumentParser()
+    parser = argparse.ArgumentParser(color=True)
     parser.add_argument('--version', action='version', version='trace 2.0')
 
     grp = parser.add_argument_group('Main options',
diff --git a/Lib/unittest/main.py b/Lib/unittest/main.py
index c3869de3f6f18e..6fd949581f3146 100644
--- a/Lib/unittest/main.py
+++ b/Lib/unittest/main.py
@@ -197,7 +197,7 @@ def _getParentArgParser(self):
         return parser
 
     def _getMainArgParser(self, parent):
-        parser = argparse.ArgumentParser(parents=[parent])
+        parser = argparse.ArgumentParser(parents=[parent], color=True)
         parser.prog = self.progName
         parser.print_help = self._print_help
 
@@ -208,7 +208,7 @@ def _getMainArgParser(self, parent):
         return parser
 
     def _getDiscoveryArgParser(self, parent):
-        parser = argparse.ArgumentParser(parents=[parent])
+        parser = argparse.ArgumentParser(parents=[parent], color=True)
         parser.prog = '%s discover' % self.progName
         parser.epilog = ('For test discovery all test modules must be '
                          'importable from the top level directory of the '
diff --git a/Lib/uuid.py b/Lib/uuid.py
index 2c16c3f0f5a5b5..036ffebf67a0be 100644
--- a/Lib/uuid.py
+++ b/Lib/uuid.py
@@ -949,7 +949,9 @@ def main():
     import argparse
     parser = argparse.ArgumentParser(
         formatter_class=argparse.ArgumentDefaultsHelpFormatter,
-        description="Generate a UUID using the selected UUID function.")
+        description="Generate a UUID using the selected UUID function.",
+        color=True,
+    )
     parser.add_argument("-u", "--uuid",
                         choices=uuid_funcs.keys(),
                         default="uuid4",
diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py
index dc4c9ef3531991..15e15b7a5184c2 100644
--- a/Lib/venv/__init__.py
+++ b/Lib/venv/__init__.py
@@ -624,7 +624,9 @@ def main(args=None):
                                             'created, you may wish to '
                                             'activate it, e.g. by '
                                             'sourcing an activate script '
-                                            'in its bin directory.')
+                                            'in its bin directory.',
+                                     color=True,
+                                     )
     parser.add_argument('dirs', metavar='ENV_DIR', nargs='+',
                         help='A directory to create the environment in.')
     parser.add_argument('--system-site-packages', default=False,
diff --git a/Lib/webbrowser.py b/Lib/webbrowser.py
index ab50ec1ee95f9e..f2e2394089d5a1 100644
--- a/Lib/webbrowser.py
+++ b/Lib/webbrowser.py
@@ -719,7 +719,9 @@ def open(self, url, new=0, autoraise=True):
 
 def parse_args(arg_list: list[str] | None):
     import argparse
-    parser = argparse.ArgumentParser(description="Open URL in a web browser.")
+    parser = argparse.ArgumentParser(
+        description="Open URL in a web browser.", color=True,
+    )
     parser.add_argument("url", help="URL to open")
 
     group = parser.add_mutually_exclusive_group()
diff --git a/Lib/zipapp.py b/Lib/zipapp.py
index 59b444075a62d0..7a4ef96ea0f077 100644
--- a/Lib/zipapp.py
+++ b/Lib/zipapp.py
@@ -187,7 +187,7 @@ def main(args=None):
     """
     import argparse
 
-    parser = argparse.ArgumentParser()
+    parser = argparse.ArgumentParser(color=True)
     parser.add_argument('--output', '-o', default=None,
             help="The name of the output archive. "
                  "Required if SOURCE is an archive.")
diff --git a/Lib/zipfile/__init__.py b/Lib/zipfile/__init__.py
index b7840d0f945a66..cfb44f3ed970ee 100644
--- a/Lib/zipfile/__init__.py
+++ b/Lib/zipfile/__init__.py
@@ -2317,7 +2317,7 @@ def main(args=None):
     import argparse
 
     description = 'A simple command-line interface for zipfile module.'
-    parser = argparse.ArgumentParser(description=description)
+    parser = argparse.ArgumentParser(description=description, color=True)
     group = parser.add_mutually_exclusive_group(required=True)
     group.add_argument('-l', '--list', metavar='<zipfile>',
                        help='Show listing of a zipfile')
diff --git 
a/Misc/NEWS.d/next/Library/2025-05-04-16-00-01.gh-issue-130645.yNwKue.rst 
b/Misc/NEWS.d/next/Library/2025-05-04-16-00-01.gh-issue-130645.yNwKue.rst
new file mode 100644
index 00000000000000..81095645cadbb8
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-05-04-16-00-01.gh-issue-130645.yNwKue.rst
@@ -0,0 +1 @@
+Add color to stdlib argparse CLIs. Patch by Hugo van Kemenade.

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com

Reply via email to