diff --git a/Lib/ast.py b/Lib/ast.py index be6ed0805d6..b9791bf52d3 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 01a76ff8e78..18f76d52ff8 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 41331dfd071..b134886dc26 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 47e2446356e..67fe370451e 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 cb6d077a391..d6d2c1386dd 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -1131,7 +1131,7 @@ class Bytecode: 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 e02e73ed722..2acb6cb79f3 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -2870,7 +2870,7 @@ __test__ = {"_TestClass": _TestClass, 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 6fc9f39b24c..aa641e94a8b 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 b7375b25473..c00f51858de 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 a2aad4c9be3..64f766f9bc2 100644 --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -1340,7 +1340,7 @@ if __name__ == '__main__': 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 9592559ba6d..52c9bb05b31 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 585583da860..de186368545 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 b5a1b8da263..33e86d51a0f 100644 --- a/Lib/mimetypes.py +++ b/Lib/mimetypes.py @@ -698,7 +698,9 @@ _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 195bfa557ef..b30df59d793 100644 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -3296,10 +3296,13 @@ To let the script run up to a given line X in the debugged file, use 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 4fa3632d1a7..beaefae0479 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 53f25ea4e46..bcddfb722bd 100644 --- a/Lib/pickletools.py +++ b/Lib/pickletools.py @@ -2842,7 +2842,9 @@ __test__ = {'disassembler_test': _dis_test, 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 507552f360b..55e211212d4 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 388614e51b1..43d8ec90ffb 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 5e5d0c4c694..86d562f0b8a 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -1011,7 +1011,7 @@ if hasattr(_os, "fork"): 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 79a6209468d..002f1986cdd 100644 --- a/Lib/sqlite3/__main__.py +++ b/Lib/sqlite3/__main__.py @@ -65,6 +65,7 @@ class SqliteInteractiveConsole(InteractiveConsole): 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 82c5f6704cb..28581f3e7a2 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='', diff --git a/Lib/test/test_ast/test_ast.py b/Lib/test/test_ast/test_ast.py index ae82395e9a0..530b5ec428e 100644 --- a/Lib/test/test_ast/test_ast.py +++ b/Lib/test/test_ast/test_ast.py @@ -3289,6 +3289,7 @@ class CommandLineTests(unittest.TestCase): 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 073df310bb4..cbfee604b7a 100644 --- a/Lib/test/test_calendar.py +++ b/Lib/test/test_calendar.py @@ -987,6 +987,7 @@ class CommandLineTestCase(unittest.TestCase): 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 dad5dbde7cd..1db3277e390 100644 --- a/Lib/test/test_mimetypes.py +++ b/Lib/test/test_mimetypes.py @@ -6,7 +6,7 @@ import sys 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 @@ class MiscTestCase(unittest.TestCase): 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 296d4b882e1..3b46e524aac 100644 --- a/Lib/test/test_pickle.py +++ b/Lib/test/test_pickle.py @@ -745,6 +745,7 @@ class CommandLineTest(unittest.TestCase): 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 b90edc05e04..818e807dd3a 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -794,6 +794,7 @@ class CommandLineTest(unittest.TestCase): 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 96f6cc86219..43957f525f1 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -1411,6 +1411,7 @@ class TestModule(unittest.TestCase): 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 dcd90d11d46..ad0dcb3cccb 100644 --- a/Lib/test/test_sqlite3/test_cli.py +++ b/Lib/test/test_sqlite3/test_cli.py @@ -4,7 +4,12 @@ import unittest 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 @@ class CommandLineInterface(unittest.TestCase): 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 117b485b934..8d01fd7bce4 100644 --- a/Lib/tokenize.py +++ b/Lib/tokenize.py @@ -518,7 +518,7 @@ def _main(args=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 a87bc6d61a8..cf8817f4383 100644 --- a/Lib/trace.py +++ b/Lib/trace.py @@ -604,7 +604,7 @@ class Trace: 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 c3869de3f6f..6fd949581f3 100644 --- a/Lib/unittest/main.py +++ b/Lib/unittest/main.py @@ -197,7 +197,7 @@ class TestProgram(object): 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 @@ class TestProgram(object): 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 2c16c3f0f5a..036ffebf67a 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 dc4c9ef3531..15e15b7a518 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 ab50ec1ee95..f2e2394089d 100644 --- a/Lib/webbrowser.py +++ b/Lib/webbrowser.py @@ -719,7 +719,9 @@ if sys.platform == "ios": 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 59b444075a6..7a4ef96ea0f 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 b7840d0f945..cfb44f3ed97 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='', 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 00000000000..81095645cad --- /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.