From aaf6fc0982c916cb71d9e0afcd7dda4ba495793b Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Wed, 20 Sep 2017 14:35:27 -0700 Subject: [PATCH] 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. --- Doc/library/argparse.rst | 7 +++- Lib/argparse.py | 2 + Lib/test/test_argparse.py | 37 ++++++++++++++++++- .../2017-09-19-13-29-29.bpo-26510.oncW6V.rst | 3 ++ 4 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2017-09-19-13-29-29.bpo-26510.oncW6V.rst diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst index c425be6d48..53e670161d 100644 --- a/Doc/library/argparse.rst +++ b/Doc/library/argparse.rst @@ -1539,8 +1539,8 @@ Sub-commands .. method:: ArgumentParser.add_subparsers([title], [description], [prog], \ [parser_class], [action], \ - [option_string], [dest], [help], \ - [metavar]) + [option_string], [dest], [required] \ + [help], [metavar]) Many programs split up their functionality into a number of sub-commands, for example, the ``svn`` program can invoke sub-commands like ``svn @@ -1576,6 +1576,9 @@ Sub-commands * dest_ - name of the attribute under which sub-command name will be stored; by default ``None`` and no value is stored + * required_ - Whether or not a subcommand must be provided, by default + ``True``. + * help_ - help for sub-parser group in help output, by default ``None`` * metavar_ - string presenting available sub-commands in help; by default it diff --git a/Lib/argparse.py b/Lib/argparse.py index d59e645203..98bbed0e50 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -1066,6 +1066,7 @@ class _SubParsersAction(Action): prog, parser_class, dest=SUPPRESS, + required=True, help=None, metavar=None): @@ -1079,6 +1080,7 @@ class _SubParsersAction(Action): dest=dest, nargs=PARSER, choices=self._name_parser_map, + required=required, help=help, metavar=metavar) diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index d8bcd7309d..c4440e4df7 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -1807,7 +1807,7 @@ class TestAddSubparsers(TestCase): 'bar', type=float, help='bar help') # check that only one subparsers argument can be added - subparsers_kwargs = {} + subparsers_kwargs = {'required': False} if aliases: subparsers_kwargs['metavar'] = 'COMMAND' subparsers_kwargs['title'] = 'commands' @@ -1907,6 +1907,41 @@ class TestAddSubparsers(TestCase): self.assertEqual(NS(foo=False, bar='1', baz='2'), parser.parse_args('1 2'.split())) + def _test_required_subparsers(self, parser): + # Should parse the sub command + ret = parser.parse_args(['run']) + self.assertEqual(ret.command, 'run') + + # Error when the command is missing + self.assertArgumentParserError(parser.parse_args, ()) + + def test_required_subparsers_via_attribute(self): + parser = ErrorRaisingArgumentParser() + subparsers = parser.add_subparsers(dest='command') + subparsers.required = True + subparsers.add_parser('run') + self._test_required_subparsers(parser) + + def test_required_subparsers_via_kwarg(self): + parser = ErrorRaisingArgumentParser() + subparsers = parser.add_subparsers(dest='command', required=True) + subparsers.add_parser('run') + self._test_required_subparsers(parser) + + def test_required_subparsers_default(self): + parser = ErrorRaisingArgumentParser() + subparsers = parser.add_subparsers(dest='command') + subparsers.add_parser('run') + self._test_required_subparsers(parser) + + def test_optional_subparsers(self): + parser = ErrorRaisingArgumentParser() + subparsers = parser.add_subparsers(dest='command', required=False) + subparsers.add_parser('run') + # No error here + ret = parser.parse_args(()) + self.assertIsNone(ret.command) + def test_help(self): self.assertEqual(self.parser.format_usage(), 'usage: PROG [-h] [--foo] bar {1,2,3} ...\n') diff --git a/Misc/NEWS.d/next/Library/2017-09-19-13-29-29.bpo-26510.oncW6V.rst b/Misc/NEWS.d/next/Library/2017-09-19-13-29-29.bpo-26510.oncW6V.rst new file mode 100644 index 0000000000..26a6b4b0ee --- /dev/null +++ b/Misc/NEWS.d/next/Library/2017-09-19-13-29-29.bpo-26510.oncW6V.rst @@ -0,0 +1,3 @@ +argparse subparsers are now required by default. This matches behaviour in Python 2. +For optional subparsers, use the new parameter ``add_subparsers(required=False)``. +Patch by Anthony Sottile. -- 2.40.0