[Python-checkins] (no subject)
Stéphane Wirtel
webhook-mailer at python.org
Fri Sep 13 06:17:47 EDT 2019
To: python-checkins at python.org
Subject: bpo-8538: Add support for boolean actions to argparse (GH-11478)
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: quoted-printable
MIME-Version: 1.0
https://github.com/python/cpython/commit/6a517c674907c195660fa9178a7b561de49c=
c721
commit: 6a517c674907c195660fa9178a7b561de49cc721
branch: master
author: R=C3=A9mi Lapeyre <remi.lapeyre at henki.fr>
committer: St=C3=A9phane Wirtel <stephane at wirtel.be>
date: 2019-09-13T11:17:43+01:00
summary:
bpo-8538: Add support for boolean actions to argparse (GH-11478)
Co-Authored-By: remilapeyre <remi.lapeyre at henki.fr>
files:
A Misc/NEWS.d/next/Library/2019-01-09-16-18-52.bpo-8538.PfVZia.rst
M Doc/library/argparse.rst
M Lib/argparse.py
M Lib/test/test_argparse.py
diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst
index 6dffd2e9325e..a8aeca41a70a 100644
--- a/Doc/library/argparse.rst
+++ b/Doc/library/argparse.rst
@@ -839,9 +839,19 @@ how the command-line arguments should be handled. The su=
pplied actions are:
Namespace(foo=3D['f1', 'f2', 'f3', 'f4'])
=20
You may also specify an arbitrary action by passing an Action subclass or
-other object that implements the same interface. The recommended way to do
-this is to extend :class:`Action`, overriding the ``__call__`` method
-and optionally the ``__init__`` method.
+other object that implements the same interface. The ``BooleanOptionalAction=
``
+is available in ``argparse`` and adds support for boolean actions such as
+``--foo`` and ``--no-foo``::
+
+ >>> import argparse
+ >>> parser =3D argparse.ArgumentParser()
+ >>> parser.add_argument('--foo', action=3Dargparse.BooleanOptionalAction)
+ >>> parser.parse_args(['--no-foo'])
+ Namespace(foo=3DFalse)
+
+The recommended way to create a custom action is to extend :class:`Action`,
+overriding the ``__call__`` method and optionally the ``__init__`` and
+``format_usage`` methods.
=20
An example of a custom action::
=20
@@ -1361,6 +1371,9 @@ Action instances should be callable, so subclasses must=
override the
The ``__call__`` method may perform arbitrary actions, but will typically set
attributes on the ``namespace`` based on ``dest`` and ``values``.
=20
+Action subclasses can define a ``format_usage`` method that takes no argument
+and return a string which will be used when printing the usage of the progra=
m.
+If such method is not provided, a sensible default will be used.
=20
The parse_args() method
-----------------------
diff --git a/Lib/argparse.py b/Lib/argparse.py
index 370692bd1bf4..13af7ac23921 100644
--- a/Lib/argparse.py
+++ b/Lib/argparse.py
@@ -67,6 +67,7 @@
'ArgumentParser',
'ArgumentError',
'ArgumentTypeError',
+ 'BooleanOptionalAction',
'FileType',
'HelpFormatter',
'ArgumentDefaultsHelpFormatter',
@@ -454,7 +455,7 @@ def _format_actions_usage(self, actions, groups):
# if the Optional doesn't take a value, format is:
# -s or --long
if action.nargs =3D=3D 0:
- part =3D '%s' % option_string
+ part =3D action.format_usage()
=20
# if the Optional takes a value, format is:
# -s ARGS or --long ARGS
@@ -842,9 +843,53 @@ def _get_kwargs(self):
]
return [(name, getattr(self, name)) for name in names]
=20
+ def format_usage(self):
+ return self.option_strings[0]
+
def __call__(self, parser, namespace, values, option_string=3DNone):
raise NotImplementedError(_('.__call__() not defined'))
=20
+class BooleanOptionalAction(Action):
+ def __init__(self,
+ option_strings,
+ dest,
+ const=3DNone,
+ default=3DNone,
+ type=3DNone,
+ choices=3DNone,
+ required=3DFalse,
+ help=3DNone,
+ metavar=3DNone):
+
+ _option_strings =3D []
+ for option_string in option_strings:
+ _option_strings.append(option_string)
+
+ if option_string.startswith('--'):
+ option_string =3D '--no-' + option_string[2:]
+ _option_strings.append(option_string)
+
+ if help is not None and default is not None:
+ help +=3D f" (default: {default})"
+
+ super().__init__(
+ option_strings=3D_option_strings,
+ dest=3Ddest,
+ nargs=3D0,
+ default=3Ddefault,
+ type=3Dtype,
+ choices=3Dchoices,
+ required=3Drequired,
+ help=3Dhelp,
+ metavar=3Dmetavar)
+
+ def __call__(self, parser, namespace, values, option_string=3DNone):
+ if option_string in self.option_strings:
+ setattr(namespace, self.dest, not option_string.startswith('--no=
-'))
+
+ def format_usage(self):
+ return ' | '.join(self.option_strings)
+
=20
class _StoreAction(Action):
=20
diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py
index 464db2906998..a97c921852c7 100644
--- a/Lib/test/test_argparse.py
+++ b/Lib/test/test_argparse.py
@@ -686,6 +686,30 @@ class TestOptionalsActionStoreTrue(ParserTestCase):
('--apple', NS(apple=3DTrue)),
]
=20
+class TestBooleanOptionalAction(ParserTestCase):
+ """Tests BooleanOptionalAction"""
+
+ argument_signatures =3D [Sig('--foo', action=3Dargparse.BooleanOptionalA=
ction)]
+ failures =3D ['--foo bar', '--foo=3Dbar']
+ successes =3D [
+ ('', NS(foo=3DNone)),
+ ('--foo', NS(foo=3DTrue)),
+ ('--no-foo', NS(foo=3DFalse)),
+ ('--foo --no-foo', NS(foo=3DFalse)), # useful for aliases
+ ('--no-foo --foo', NS(foo=3DTrue)),
+ ]
+
+class TestBooleanOptionalActionRequired(ParserTestCase):
+ """Tests BooleanOptionalAction required"""
+
+ argument_signatures =3D [
+ Sig('--foo', required=3DTrue, action=3Dargparse.BooleanOptionalActio=
n)
+ ]
+ failures =3D ['']
+ successes =3D [
+ ('--foo', NS(foo=3DTrue)),
+ ('--no-foo', NS(foo=3DFalse)),
+ ]
=20
class TestOptionalsActionAppend(ParserTestCase):
"""Tests the append action for an Optional"""
@@ -3456,6 +3480,10 @@ class TestHelpUsage(HelpTestCase):
Sig('a', help=3D'a'),
Sig('b', help=3D'b', nargs=3D2),
Sig('c', help=3D'c', nargs=3D'?'),
+ Sig('--foo', help=3D'Whether to foo', action=3Dargparse.BooleanOptio=
nalAction),
+ Sig('--bar', help=3D'Whether to bar', default=3DTrue,
+ action=3Dargparse.BooleanOptionalAction),
+ Sig('-f', '--foobar', '--barfoo', action=3Dargparse.BooleanOptionalA=
ction),
]
argument_group_signatures =3D [
(Sig('group'), [
@@ -3466,26 +3494,32 @@ class TestHelpUsage(HelpTestCase):
])
]
usage =3D '''\
- usage: PROG [-h] [-w W [W ...]] [-x [X [X ...]]] [-y [Y]] [-z Z Z Z]
+ usage: PROG [-h] [-w W [W ...]] [-x [X [X ...]]] [--foo | --no-foo]
+ [--bar | --no-bar]
+ [-f | --foobar | --no-foobar | --barfoo | --no-barfoo] [=
-y [Y]]
+ [-z Z Z Z]
a b b [c] [d [d ...]] e [e ...]
'''
help =3D usage + '''\
=20
positional arguments:
- a a
- b b
- c c
+ a a
+ b b
+ c c
=20
optional arguments:
- -h, --help show this help message and exit
- -w W [W ...] w
- -x [X [X ...]] x
+ -h, --help show this help message and exit
+ -w W [W ...] w
+ -x [X [X ...]] x
+ --foo, --no-foo Whether to foo
+ --bar, --no-bar Whether to bar (default: True)
+ -f, --foobar, --no-foobar, --barfoo, --no-barfoo
=20
group:
- -y [Y] y
- -z Z Z Z z
- d d
- e e
+ -y [Y] y
+ -z Z Z Z z
+ d d
+ e e
'''
version =3D ''
=20
diff --git a/Misc/NEWS.d/next/Library/2019-01-09-16-18-52.bpo-8538.PfVZia.rst=
b/Misc/NEWS.d/next/Library/2019-01-09-16-18-52.bpo-8538.PfVZia.rst
new file mode 100644
index 000000000000..94249ab1e434
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2019-01-09-16-18-52.bpo-8538.PfVZia.rst
@@ -0,0 +1,2 @@
+Add support for boolean actions like ``--foo`` and ``--no-foo`` to argparse.
+Patch contributed by R=C3=A9mi Lapeyre.
More information about the Python-checkins
mailing list