Skip to content

Commit acbe896

Browse files
pablogsalhugovkambvAA-Turner
authored
GH-130645: Default to color help in argparse (#136809)
Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Co-authored-by: Łukasz Langa <lukasz@langa.pl> Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com>
1 parent 65d2c51 commit acbe896

File tree

6 files changed

+39
-25
lines changed

6 files changed

+39
-25
lines changed

Doc/library/argparse.rst

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ ArgumentParser objects
7474
prefix_chars='-', fromfile_prefix_chars=None, \
7575
argument_default=None, conflict_handler='error', \
7676
add_help=True, allow_abbrev=True, exit_on_error=True, \
77-
*, suggest_on_error=False, color=False)
77+
*, suggest_on_error=False, color=True)
7878
7979
Create a new :class:`ArgumentParser` object. All parameters should be passed
8080
as keyword arguments. Each parameter has its own more detailed description
@@ -119,7 +119,7 @@ ArgumentParser objects
119119
* suggest_on_error_ - Enables suggestions for mistyped argument choices
120120
and subparser names (default: ``False``)
121121

122-
* color_ - Allow color output (default: ``False``)
122+
* color_ - Allow color output (default: ``True``)
123123

124124
.. versionchanged:: 3.5
125125
*allow_abbrev* parameter was added.
@@ -626,27 +626,19 @@ keyword argument::
626626
color
627627
^^^^^
628628

629-
By default, the help message is printed in plain text. If you want to allow
630-
color in help messages, you can enable it by setting ``color`` to ``True``::
629+
By default, the help message is printed in color using `ANSI escape sequences
630+
<https://en.wikipedia.org/wiki/ANSI_escape_code>`__.
631+
If you want plain text help messages, you can disable this :ref:`in your local
632+
environment <using-on-controlling-color>`, or in the argument parser itself
633+
by setting ``color`` to ``False``::
631634

632635
>>> parser = argparse.ArgumentParser(description='Process some integers.',
633-
... color=True)
636+
... color=False)
634637
>>> parser.add_argument('--action', choices=['sum', 'max'])
635638
>>> parser.add_argument('integers', metavar='N', type=int, nargs='+',
636639
... help='an integer for the accumulator')
637640
>>> parser.parse_args(['--help'])
638641

639-
Even if a CLI author has enabled color, it can be
640-
:ref:`controlled using environment variables <using-on-controlling-color>`.
641-
642-
If you're writing code that needs to be compatible with older Python versions
643-
and want to opportunistically use ``color`` when it's available, you
644-
can set it as an attribute after initializing the parser instead of using the
645-
keyword argument::
646-
647-
>>> parser = argparse.ArgumentParser(description='Process some integers.')
648-
>>> parser.color = True
649-
650642
.. versionadded:: 3.14
651643

652644

Doc/whatsnew/3.14.rst

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1228,11 +1228,10 @@ argparse
12281228

12291229
.. _whatsnew314-color-argparse:
12301230

1231-
* Introduced the optional *color* parameter to
1232-
:class:`argparse.ArgumentParser`, enabling color for help text.
1233-
This can be controlled by :ref:`environment variables
1234-
<using-on-controlling-color>`. Color has also been enabled for help in the
1235-
:ref:`stdlib CLIs <library-cmdline>` which use :mod:`!argparse`.
1231+
* Enable color for help text, which can be disabled with the optional *color*
1232+
parameter to :class:`argparse.ArgumentParser`.
1233+
This can also be controlled by :ref:`environment variables
1234+
<using-on-controlling-color>`.
12361235
(Contributed by Hugo van Kemenade in :gh:`130645`.)
12371236

12381237

Lib/argparse.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ def __init__(
167167
indent_increment=2,
168168
max_help_position=24,
169169
width=None,
170-
color=False,
170+
color=True,
171171
):
172172
# default setting for width
173173
if width is None:
@@ -1231,7 +1231,7 @@ def __init__(self,
12311231
self._name_parser_map = {}
12321232
self._choices_actions = []
12331233
self._deprecated = set()
1234-
self._color = False
1234+
self._color = True
12351235

12361236
super(_SubParsersAction, self).__init__(
12371237
option_strings=option_strings,
@@ -1878,7 +1878,7 @@ def __init__(self,
18781878
exit_on_error=True,
18791879
*,
18801880
suggest_on_error=False,
1881-
color=False,
1881+
color=True,
18821882
):
18831883
superinit = super(ArgumentParser, self).__init__
18841884
superinit(description=description,

Lib/test/test_argparse.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@
1818
import warnings
1919

2020
from enum import StrEnum
21-
from test.support import captured_stderr
21+
from test.support import (
22+
captured_stderr,
23+
force_not_colorized,
24+
force_not_colorized_test_class,
25+
)
2226
from test.support import import_helper
2327
from test.support import os_helper
2428
from test.support import script_helper
@@ -1007,6 +1011,7 @@ def test_parse_enum_value(self):
10071011
args = parser.parse_args(['--color', 'red'])
10081012
self.assertEqual(args.color, self.Color.RED)
10091013

1014+
@force_not_colorized
10101015
def test_help_message_contains_enum_choices(self):
10111016
parser = argparse.ArgumentParser()
10121017
parser.add_argument('--color', choices=self.Color, help='Choose a color')
@@ -2403,6 +2408,7 @@ def test_modified_invalid_action(self):
24032408
# Subparsers tests
24042409
# ================
24052410

2411+
@force_not_colorized_test_class
24062412
class TestAddSubparsers(TestCase):
24072413
"""Test the add_subparsers method"""
24082414

@@ -3009,6 +3015,7 @@ def test_nested_argument_group(self):
30093015
# Parent parser tests
30103016
# ===================
30113017

3018+
@force_not_colorized_test_class
30123019
class TestParentParsers(TestCase):
30133020
"""Tests that parsers can be created with parent parsers"""
30143021

@@ -3216,6 +3223,7 @@ def test_mutex_groups_parents(self):
32163223
# Mutually exclusive group tests
32173224
# ==============================
32183225

3226+
@force_not_colorized_test_class
32193227
class TestMutuallyExclusiveGroupErrors(TestCase):
32203228

32213229
def test_invalid_add_argument_group(self):
@@ -3344,21 +3352,25 @@ def test_successes_when_required(self):
33443352
actual_ns = parse_args(args_string.split())
33453353
self.assertEqual(actual_ns, expected_ns)
33463354

3355+
@force_not_colorized
33473356
def test_usage_when_not_required(self):
33483357
format_usage = self.get_parser(required=False).format_usage
33493358
expected_usage = self.usage_when_not_required
33503359
self.assertEqual(format_usage(), textwrap.dedent(expected_usage))
33513360

3361+
@force_not_colorized
33523362
def test_usage_when_required(self):
33533363
format_usage = self.get_parser(required=True).format_usage
33543364
expected_usage = self.usage_when_required
33553365
self.assertEqual(format_usage(), textwrap.dedent(expected_usage))
33563366

3367+
@force_not_colorized
33573368
def test_help_when_not_required(self):
33583369
format_help = self.get_parser(required=False).format_help
33593370
help = self.usage_when_not_required + self.help
33603371
self.assertEqual(format_help(), textwrap.dedent(help))
33613372

3373+
@force_not_colorized
33623374
def test_help_when_required(self):
33633375
format_help = self.get_parser(required=True).format_help
33643376
help = self.usage_when_required + self.help
@@ -4030,11 +4042,13 @@ def _test(self, tester, parser_text):
40304042
tester.maxDiff = None
40314043
tester.assertEqual(expected_text, parser_text)
40324044

4045+
@force_not_colorized
40334046
def test_format(self, tester):
40344047
parser = self._get_parser(tester)
40354048
format = getattr(parser, 'format_%s' % self.func_suffix)
40364049
self._test(tester, format())
40374050

4051+
@force_not_colorized
40384052
def test_print(self, tester):
40394053
parser = self._get_parser(tester)
40404054
print_ = getattr(parser, 'print_%s' % self.func_suffix)
@@ -4047,6 +4061,7 @@ def test_print(self, tester):
40474061
setattr(sys, self.std_name, old_stream)
40484062
self._test(tester, parser_text)
40494063

4064+
@force_not_colorized
40504065
def test_print_file(self, tester):
40514066
parser = self._get_parser(tester)
40524067
print_ = getattr(parser, 'print_%s' % self.func_suffix)
@@ -4788,6 +4803,7 @@ class TestHelpUsageMetavarsSpacesParentheses(HelpTestCase):
47884803
version = ''
47894804

47904805

4806+
@force_not_colorized_test_class
47914807
class TestHelpUsageNoWhitespaceCrash(TestCase):
47924808

47934809
def test_all_suppressed_mutex_followed_by_long_arg(self):
@@ -5469,6 +5485,7 @@ def custom_type(string):
54695485
version = ''
54705486

54715487

5488+
@force_not_colorized_test_class
54725489
class TestHelpCustomHelpFormatter(TestCase):
54735490
maxDiff = None
54745491

@@ -5765,6 +5782,7 @@ def test_conflict_error(self):
57655782
self.assertRaises(argparse.ArgumentError,
57665783
parser.add_argument, '--spam')
57675784

5785+
@force_not_colorized
57685786
def test_resolve_error(self):
57695787
get_parser = argparse.ArgumentParser
57705788
parser = get_parser(prog='PROG', conflict_handler='resolve')
@@ -6031,6 +6049,7 @@ def test_argument_error(self):
60316049

60326050
class TestArgumentTypeError(TestCase):
60336051

6052+
@force_not_colorized
60346053
def test_argument_type_error(self):
60356054

60366055
def spam(string):
@@ -6829,6 +6848,7 @@ def setUp(self):
68296848
metavar = '<http[s]://example:1234>'
68306849
self.parser.add_argument('--proxy', metavar=metavar)
68316850

6851+
@force_not_colorized
68326852
def test_help_with_metavar(self):
68336853
help_text = self.parser.format_help()
68346854
self.assertEqual(help_text, textwrap.dedent('''\
@@ -6994,6 +7014,7 @@ def test_os_error(self):
69947014
self.parser.parse_args, ['@no-such-file'])
69957015

69967016

7017+
@force_not_colorized_test_class
69977018
class TestProgName(TestCase):
69987019
source = textwrap.dedent('''\
69997020
import argparse

Lib/test/test_clinic.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2792,6 +2792,7 @@ def test_cli_verbose(self):
27922792
out = self.expect_success("-v", fn)
27932793
self.assertEqual(out.strip(), fn)
27942794

2795+
@support.force_not_colorized
27952796
def test_cli_help(self):
27962797
out = self.expect_success("-h")
27972798
self.assertIn("usage: clinic.py", out)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Enable color help by default in :mod:`argparse`.

0 commit comments

Comments
 (0)