]> granicus.if.org Git - python/commitdiff
bpo-26510: make argparse subparsers required by default (#3027)
authorAnthony Sottile <asottile@umich.edu>
Wed, 20 Sep 2017 21:35:27 +0000 (14:35 -0700)
committerÉric Araujo <merwok@users.noreply.github.com>
Wed, 20 Sep 2017 21:35:27 +0000 (17:35 -0400)
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
Lib/argparse.py
Lib/test/test_argparse.py
Misc/NEWS.d/next/Library/2017-09-19-13-29-29.bpo-26510.oncW6V.rst [new file with mode: 0644]

index c425be6d481dba31a869c9f8bad0771afcaa91f5..53e670161dd5b36ae8a5353f430aa732c0c71195 100644 (file)
@@ -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
index d59e645203c0e434bff7d99e5e8c9a9cd88cf842..98bbed0e50821af6686463f4cc78899fc8f6d4be 100644 (file)
@@ -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)
 
index d8bcd7309d2e299ca29ff142110f3b4c34e03857..c4440e4df7c15b680a0473b3f86ec573fec62bf7 100644 (file)
@@ -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 (file)
index 0000000..26a6b4b
--- /dev/null
@@ -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.