diff -r b7f2d28260b4 Doc/library/argparse.rst --- a/Doc/library/argparse.rst Tue Apr 09 17:23:27 2013 +0200 +++ b/Doc/library/argparse.rst Fri Apr 12 22:49:30 2013 -0700 @@ -1452,7 +1452,26 @@ Sub-commands ^^^^^^^^^^^^ -.. method:: ArgumentParser.add_subparsers() +.. method:: ArgumentParser.add_subparsers([title], [description], [prog], [dest], \ + [required], [help], [metavar]) + + Create an action object which parses a command-line argument as a choice + from a set of parser sub-commands. The keyword arguments include a subset of + those accepted by :meth:`ArgumentParser.add_argument`. + + * title_ - If defined, display the sub-command choices as an argument group. + + * `description` - Description of the argument group. + + * `prog` - Usage prefix in the subparser help output (default uses the parser prog_). + + * dest_ - :class:`Namespace` attribute to hold the sub-command name (default ``argparse.SUPPRESS``). + + * required_ - Whether or not the command-line option may be omitted (default ``True``). + + * help_ - A brief description of subparser group. + + * metavar_ - A name for the sub-command in the usage message. Many programs split up their functionality into a number of sub-commands, for example, the ``svn`` program can invoke sub-commands like ``svn @@ -1492,7 +1511,8 @@ command line (and not any other subparsers). So in the example above, when the ``a`` command is specified, only the ``foo`` and ``bar`` attributes are present, and when the ``b`` command is specified, only the ``foo`` and - ``baz`` attributes are present. + ``baz`` attributes are present. In this example, all argument strings after + subparser name ('a' or 'b') are parsed by that subparser. Similarly, when a help message is requested from a subparser, only the help for that particular parser will be printed. The help message will not @@ -1530,6 +1550,8 @@ -h, --help show this help message and exit --baz {X,Y,Z} baz help +.. _title: + The :meth:`add_subparsers` method also supports ``title`` and ``description`` keyword arguments. When either is present, the subparser's commands will appear in their own group in the help output. For example:: diff -r b7f2d28260b4 Lib/argparse.py --- a/Lib/argparse.py Tue Apr 09 17:23:27 2013 +0200 +++ b/Lib/argparse.py Fri Apr 12 22:49:30 2013 -0700 @@ -696,6 +696,8 @@ return argument.metavar elif argument.dest not in (None, SUPPRESS): return argument.dest + elif hasattr(argument, 'name'): + return argument.name() else: return None @@ -1057,6 +1059,7 @@ prog, parser_class, dest=SUPPRESS, + required=True, help=None, metavar=None): @@ -1070,6 +1073,7 @@ dest=dest, nargs=PARSER, choices=self._name_parser_map, + required=required, help=help, metavar=metavar) @@ -1124,6 +1128,9 @@ vars(namespace).setdefault(_UNRECOGNIZED_ARGS_ATTR, []) getattr(namespace, _UNRECOGNIZED_ARGS_ATTR).extend(arg_strings) + def name(self): + # custom name for _get_action_name() + return "{%s}"%','.join(self._name_parser_map) # ============== # Type classes @@ -1977,6 +1984,7 @@ self._get_value(action, action.default)) if required_actions: + required_actions = ['%s'%name for name in required_actions] self.error(_('the following arguments are required: %s') % ', '.join(required_actions)) @@ -1992,6 +2000,7 @@ names = [_get_action_name(action) for action in group._group_actions if action.help is not SUPPRESS] + names = ['%s'%name for name in names] msg = _('one of the arguments %s is required') self.error(msg % ' '.join(names)) diff -r b7f2d28260b4 Lib/test/test_argparse.py --- a/Lib/test/test_argparse.py Tue Apr 09 17:23:27 2013 +0200 +++ b/Lib/test/test_argparse.py Fri Apr 12 22:49:30 2013 -0700 @@ -1833,6 +1833,31 @@ args = args_str.split() self.assertArgumentParserError(self.parser.parse_args, args) + def test_parse_args_missing_cmd(self): + # test error msg when cmd is missing + for args_str,reg_str in [['0.5','the following arguments are required']]: + args = args_str.split() + self.assertRaisesRegexp(ArgumentParserError, reg_str, + self.parser.parse_args, args) + # was - no error (effectively optional argument) + + def test_parse_args_invalid_choice(self): + # test error msg when cmd is wrong choice + for args_str,reg_str in [['0.5 0','error: argument {1,2,3}: invalid choice:']]: + args = args_str.split() + self.assertRaisesRegexp(ArgumentParserError, reg_str, + self.parser.parse_args, args) + # was - 'error: invalid choice:' + + def test_required(self): + # test ability to change 'required'; default is True + parser = ErrorRaisingArgumentParser() + parser.add_argument('--foo', action='store_true') + subparsers = parser.add_subparsers(required=False) + parser1 = subparsers.add_parser('1') + parser1.add_argument('baz') + self.assertEqual(NS(foo=False), parser.parse_args([])) + def test_parse_args(self): # check some non-failure cases: self.assertEqual( @@ -2042,6 +2067,10 @@ parser = self._get_parser(aliases=True) self.assertArgumentParserError(parser.parse_args, '0.5 1alias3 b'.split()) + # with metavar, the error message has not changed + self.assertRaisesRegexp(ArgumentParserError, + 'error: argument COMMAND: invalid choice:', + parser.parse_args, '0.5 1alias3 b'.split()) def test_alias_help(self): parser = self._get_parser(aliases=True, subparser_help=True)