Skip to content

Commit aaf6fc0

Browse files
asottilemerwok
authored andcommitted
bpo-26510: make argparse subparsers required by default (#3027)
This fixes a regression from Python 2. To get optional subparsers, use the new parameter ``add_subparsers(required=False)``. Patch by Anthony Sottile.
1 parent 19e4d93 commit aaf6fc0

File tree

4 files changed

+46
-3
lines changed

4 files changed

+46
-3
lines changed

Doc/library/argparse.rst

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1539,8 +1539,8 @@ Sub-commands
15391539

15401540
.. method:: ArgumentParser.add_subparsers([title], [description], [prog], \
15411541
[parser_class], [action], \
1542-
[option_string], [dest], [help], \
1543-
[metavar])
1542+
[option_string], [dest], [required] \
1543+
[help], [metavar])
15441544

15451545
Many programs split up their functionality into a number of sub-commands,
15461546
for example, the ``svn`` program can invoke sub-commands like ``svn
@@ -1576,6 +1576,9 @@ Sub-commands
15761576
* dest_ - name of the attribute under which sub-command name will be
15771577
stored; by default ``None`` and no value is stored
15781578

1579+
* required_ - Whether or not a subcommand must be provided, by default
1580+
``True``.
1581+
15791582
* help_ - help for sub-parser group in help output, by default ``None``
15801583

15811584
* metavar_ - string presenting available sub-commands in help; by default it

Lib/argparse.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,6 +1066,7 @@ def __init__(self,
10661066
prog,
10671067
parser_class,
10681068
dest=SUPPRESS,
1069+
required=True,
10691070
help=None,
10701071
metavar=None):
10711072

@@ -1079,6 +1080,7 @@ def __init__(self,
10791080
dest=dest,
10801081
nargs=PARSER,
10811082
choices=self._name_parser_map,
1083+
required=required,
10821084
help=help,
10831085
metavar=metavar)
10841086

Lib/test/test_argparse.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1807,7 +1807,7 @@ def _get_parser(self, subparser_help=False, prefix_chars=None,
18071807
'bar', type=float, help='bar help')
18081808

18091809
# check that only one subparsers argument can be added
1810-
subparsers_kwargs = {}
1810+
subparsers_kwargs = {'required': False}
18111811
if aliases:
18121812
subparsers_kwargs['metavar'] = 'COMMAND'
18131813
subparsers_kwargs['title'] = 'commands'
@@ -1907,6 +1907,41 @@ def test_dest(self):
19071907
self.assertEqual(NS(foo=False, bar='1', baz='2'),
19081908
parser.parse_args('1 2'.split()))
19091909

1910+
def _test_required_subparsers(self, parser):
1911+
# Should parse the sub command
1912+
ret = parser.parse_args(['run'])
1913+
self.assertEqual(ret.command, 'run')
1914+
1915+
# Error when the command is missing
1916+
self.assertArgumentParserError(parser.parse_args, ())
1917+
1918+
def test_required_subparsers_via_attribute(self):
1919+
parser = ErrorRaisingArgumentParser()
1920+
subparsers = parser.add_subparsers(dest='command')
1921+
subparsers.required = True
1922+
subparsers.add_parser('run')
1923+
self._test_required_subparsers(parser)
1924+
1925+
def test_required_subparsers_via_kwarg(self):
1926+
parser = ErrorRaisingArgumentParser()
1927+
subparsers = parser.add_subparsers(dest='command', required=True)
1928+
subparsers.add_parser('run')
1929+
self._test_required_subparsers(parser)
1930+
1931+
def test_required_subparsers_default(self):
1932+
parser = ErrorRaisingArgumentParser()
1933+
subparsers = parser.add_subparsers(dest='command')
1934+
subparsers.add_parser('run')
1935+
self._test_required_subparsers(parser)
1936+
1937+
def test_optional_subparsers(self):
1938+
parser = ErrorRaisingArgumentParser()
1939+
subparsers = parser.add_subparsers(dest='command', required=False)
1940+
subparsers.add_parser('run')
1941+
# No error here
1942+
ret = parser.parse_args(())
1943+
self.assertIsNone(ret.command)
1944+
19101945
def test_help(self):
19111946
self.assertEqual(self.parser.format_usage(),
19121947
'usage: PROG [-h] [--foo] bar {1,2,3} ...\n')
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
argparse subparsers are now required by default. This matches behaviour in Python 2.
2+
For optional subparsers, use the new parameter ``add_subparsers(required=False)``.
3+
Patch by Anthony Sottile.

0 commit comments

Comments
 (0)