https://github.com/python/cpython/commit/17c5959aa3daee4b63d78e944a1d355daa651e52
commit: 17c5959aa3daee4b63d78e944a1d355daa651e52
branch: 3.14
author: Miss Islington (bot) <31488909+miss-isling...@users.noreply.github.com>
committer: savannahostrowski <savannahostrow...@gmail.com>
date: 2025-07-20T15:22:53-07:00
summary:

[3.14] GH-130645: Default to color help in argparse (GH-136809) (#136886)

GH-130645: Default to color help in argparse (GH-136809)
(cherry picked from commit acbe896cb12d6a92e6150fff22921756f6928035)

Co-authored-by: Pablo Galindo Salgado <pablog...@gmail.com>
Co-authored-by: Hugo van Kemenade <1324225+hug...@users.noreply.github.com>
Co-authored-by: Ɓukasz Langa <luk...@langa.pl>
Co-authored-by: Adam Turner <9087854+aa-tur...@users.noreply.github.com>

files:
A Misc/NEWS.d/next/Library/2025-07-19-16-20-54.gh-issue-130645.O-dYcN.rst
M Doc/library/argparse.rst
M Doc/whatsnew/3.14.rst
M Lib/argparse.py
M Lib/test/test_argparse.py
M Lib/test/test_clinic.py

diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst
index a08f713ab56ba3..79e15994491eff 100644
--- a/Doc/library/argparse.rst
+++ b/Doc/library/argparse.rst
@@ -74,7 +74,7 @@ ArgumentParser objects
                           prefix_chars='-', fromfile_prefix_chars=None, \
                           argument_default=None, conflict_handler='error', \
                           add_help=True, allow_abbrev=True, 
exit_on_error=True, \
-                          *, suggest_on_error=False, color=False)
+                          *, suggest_on_error=False, color=True)
 
    Create a new :class:`ArgumentParser` object. All parameters should be passed
    as keyword arguments. Each parameter has its own more detailed description
@@ -119,7 +119,7 @@ ArgumentParser objects
    * suggest_on_error_ - Enables suggestions for mistyped argument choices
      and subparser names (default: ``False``)
 
-   * color_ - Allow color output (default: ``False``)
+   * color_ - Allow color output (default: ``True``)
 
    .. versionchanged:: 3.5
       *allow_abbrev* parameter was added.
@@ -626,27 +626,19 @@ keyword argument::
 color
 ^^^^^
 
-By default, the help message is printed in plain text. If you want to allow
-color in help messages, you can enable it by setting ``color`` to ``True``::
+By default, the help message is printed in color using `ANSI escape sequences
+<https://en.wikipedia.org/wiki/ANSI_escape_code>`__.
+If you want plain text help messages, you can disable this :ref:`in your local
+environment <using-on-controlling-color>`, or in the argument parser itself
+by setting ``color`` to ``False``::
 
    >>> parser = argparse.ArgumentParser(description='Process some integers.',
-   ...                                  color=True)
+   ...                                  color=False)
    >>> parser.add_argument('--action', choices=['sum', 'max'])
    >>> parser.add_argument('integers', metavar='N', type=int, nargs='+',
    ...                     help='an integer for the accumulator')
    >>> parser.parse_args(['--help'])
 
-Even if a CLI author has enabled color, it can be
-:ref:`controlled using environment variables <using-on-controlling-color>`.
-
-If you're writing code that needs to be compatible with older Python versions
-and want to opportunistically use ``color`` when it's available, you
-can set it as an attribute after initializing the parser instead of using the
-keyword argument::
-
-   >>> parser = argparse.ArgumentParser(description='Process some integers.')
-   >>> parser.color = True
-
 .. versionadded:: 3.14
 
 
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index ca9aa0b55605f1..b85440736963cb 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -1228,11 +1228,10 @@ argparse
 
   .. _whatsnew314-color-argparse:
 
-* Introduced the optional *color* parameter to
-  :class:`argparse.ArgumentParser`, enabling color for help text.
-  This can be controlled by :ref:`environment variables
-  <using-on-controlling-color>`. Color has also been enabled for help in the
-  :ref:`stdlib CLIs <library-cmdline>` which use :mod:`!argparse`.
+* Enable color for help text, which can be disabled with the optional *color*
+  parameter to :class:`argparse.ArgumentParser`.
+  This can also be controlled by :ref:`environment variables
+  <using-on-controlling-color>`.
   (Contributed by Hugo van Kemenade in :gh:`130645`.)
 
 
diff --git a/Lib/argparse.py b/Lib/argparse.py
index 83258cf3e0f37d..2144c81886ad19 100644
--- a/Lib/argparse.py
+++ b/Lib/argparse.py
@@ -167,7 +167,7 @@ def __init__(
         indent_increment=2,
         max_help_position=24,
         width=None,
-        color=False,
+        color=True,
     ):
         # default setting for width
         if width is None:
@@ -1231,7 +1231,7 @@ def __init__(self,
         self._name_parser_map = {}
         self._choices_actions = []
         self._deprecated = set()
-        self._color = False
+        self._color = True
 
         super(_SubParsersAction, self).__init__(
             option_strings=option_strings,
@@ -1878,7 +1878,7 @@ def __init__(self,
                  exit_on_error=True,
                  *,
                  suggest_on_error=False,
-                 color=False,
+                 color=True,
                  ):
         superinit = super(ArgumentParser, self).__init__
         superinit(description=description,
diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py
index 08ff41368d9bb0..2f39b42ab74299 100644
--- a/Lib/test/test_argparse.py
+++ b/Lib/test/test_argparse.py
@@ -18,7 +18,11 @@
 import warnings
 
 from enum import StrEnum
-from test.support import captured_stderr
+from test.support import (
+    captured_stderr,
+    force_not_colorized,
+    force_not_colorized_test_class,
+)
 from test.support import import_helper
 from test.support import os_helper
 from test.support import script_helper
@@ -1007,6 +1011,7 @@ def test_parse_enum_value(self):
         args = parser.parse_args(['--color', 'red'])
         self.assertEqual(args.color, self.Color.RED)
 
+    @force_not_colorized
     def test_help_message_contains_enum_choices(self):
         parser = argparse.ArgumentParser()
         parser.add_argument('--color', choices=self.Color, help='Choose a 
color')
@@ -2403,6 +2408,7 @@ def test_modified_invalid_action(self):
 # Subparsers tests
 # ================
 
+@force_not_colorized_test_class
 class TestAddSubparsers(TestCase):
     """Test the add_subparsers method"""
 
@@ -3009,6 +3015,7 @@ def test_nested_argument_group(self):
 # Parent parser tests
 # ===================
 
+@force_not_colorized_test_class
 class TestParentParsers(TestCase):
     """Tests that parsers can be created with parent parsers"""
 
@@ -3216,6 +3223,7 @@ def test_mutex_groups_parents(self):
 # Mutually exclusive group tests
 # ==============================
 
+@force_not_colorized_test_class
 class TestMutuallyExclusiveGroupErrors(TestCase):
 
     def test_invalid_add_argument_group(self):
@@ -3344,21 +3352,25 @@ def test_successes_when_required(self):
                 actual_ns = parse_args(args_string.split())
                 self.assertEqual(actual_ns, expected_ns)
 
+    @force_not_colorized
     def test_usage_when_not_required(self):
         format_usage = self.get_parser(required=False).format_usage
         expected_usage = self.usage_when_not_required
         self.assertEqual(format_usage(), textwrap.dedent(expected_usage))
 
+    @force_not_colorized
     def test_usage_when_required(self):
         format_usage = self.get_parser(required=True).format_usage
         expected_usage = self.usage_when_required
         self.assertEqual(format_usage(), textwrap.dedent(expected_usage))
 
+    @force_not_colorized
     def test_help_when_not_required(self):
         format_help = self.get_parser(required=False).format_help
         help = self.usage_when_not_required + self.help
         self.assertEqual(format_help(), textwrap.dedent(help))
 
+    @force_not_colorized
     def test_help_when_required(self):
         format_help = self.get_parser(required=True).format_help
         help = self.usage_when_required + self.help
@@ -4030,11 +4042,13 @@ def _test(self, tester, parser_text):
                 tester.maxDiff = None
                 tester.assertEqual(expected_text, parser_text)
 
+            @force_not_colorized
             def test_format(self, tester):
                 parser = self._get_parser(tester)
                 format = getattr(parser, 'format_%s' % self.func_suffix)
                 self._test(tester, format())
 
+            @force_not_colorized
             def test_print(self, tester):
                 parser = self._get_parser(tester)
                 print_ = getattr(parser, 'print_%s' % self.func_suffix)
@@ -4047,6 +4061,7 @@ def test_print(self, tester):
                     setattr(sys, self.std_name, old_stream)
                 self._test(tester, parser_text)
 
+            @force_not_colorized
             def test_print_file(self, tester):
                 parser = self._get_parser(tester)
                 print_ = getattr(parser, 'print_%s' % self.func_suffix)
@@ -4788,6 +4803,7 @@ class 
TestHelpUsageMetavarsSpacesParentheses(HelpTestCase):
     version = ''
 
 
+@force_not_colorized_test_class
 class TestHelpUsageNoWhitespaceCrash(TestCase):
 
     def test_all_suppressed_mutex_followed_by_long_arg(self):
@@ -5469,6 +5485,7 @@ def custom_type(string):
     version = ''
 
 
+@force_not_colorized_test_class
 class TestHelpCustomHelpFormatter(TestCase):
     maxDiff = None
 
@@ -5765,6 +5782,7 @@ def test_conflict_error(self):
         self.assertRaises(argparse.ArgumentError,
                           parser.add_argument, '--spam')
 
+    @force_not_colorized
     def test_resolve_error(self):
         get_parser = argparse.ArgumentParser
         parser = get_parser(prog='PROG', conflict_handler='resolve')
@@ -6031,6 +6049,7 @@ def test_argument_error(self):
 
 class TestArgumentTypeError(TestCase):
 
+    @force_not_colorized
     def test_argument_type_error(self):
 
         def spam(string):
@@ -6829,6 +6848,7 @@ def setUp(self):
         metavar = '<http[s]://example:1234>'
         self.parser.add_argument('--proxy', metavar=metavar)
 
+    @force_not_colorized
     def test_help_with_metavar(self):
         help_text = self.parser.format_help()
         self.assertEqual(help_text, textwrap.dedent('''\
@@ -6994,6 +7014,7 @@ def test_os_error(self):
                                self.parser.parse_args, ['@no-such-file'])
 
 
+@force_not_colorized_test_class
 class TestProgName(TestCase):
     source = textwrap.dedent('''\
         import argparse
diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py
index 4b1f5991a39ee8..d5b751eafc5bbb 100644
--- a/Lib/test/test_clinic.py
+++ b/Lib/test/test_clinic.py
@@ -2792,6 +2792,7 @@ def test_cli_verbose(self):
             out = self.expect_success("-v", fn)
             self.assertEqual(out.strip(), fn)
 
+    @support.force_not_colorized
     def test_cli_help(self):
         out = self.expect_success("-h")
         self.assertIn("usage: clinic.py", out)
diff --git 
a/Misc/NEWS.d/next/Library/2025-07-19-16-20-54.gh-issue-130645.O-dYcN.rst 
b/Misc/NEWS.d/next/Library/2025-07-19-16-20-54.gh-issue-130645.O-dYcN.rst
new file mode 100644
index 00000000000000..96e076dfe5bd12
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-07-19-16-20-54.gh-issue-130645.O-dYcN.rst
@@ -0,0 +1 @@
+Enable color help by default in :mod:`argparse`.

_______________________________________________
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