cpython: 97b28115bd08 (original) (raw)
--- a/Doc/library/argparse.rst +++ b/Doc/library/argparse.rst @@ -1668,6 +1668,18 @@ Sub-commands >>> parser.parse_args(['co', 'bar']) Namespace(foo='bar')
- argparse supports non-ambiguous abbreviations of subparser names. +
- For example, the following three calls are all supported
- and do the same thing, as the abbreviations used are not ambiguous:: +
>>> parser.parse_args(['checkout', 'bar'])[](#l1.12)
Namespace(foo='bar')[](#l1.13)
>>> parser.parse_args(['check', 'bar'])[](#l1.14)
Namespace(foo='bar')[](#l1.15)
>>> parser.parse_args(['che', 'bar'])[](#l1.16)
Namespace(foo='bar')[](#l1.17)
+
One particularly effective way of handling sub-commands is to combine the use
of the :meth:add_subparsers
method with calls to :meth:set_defaults
so
that each subparser knows which Python function it should execute. For
--- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -1110,6 +1110,12 @@ class _SubParsersAction(Action): parser_name = values[0] arg_strings = values[1:]
# get full parser_name from (optional) abbreviated one[](#l2.7)
for p in self._name_parser_map:[](#l2.8)
if p.startswith(parser_name):[](#l2.9)
parser_name = p[](#l2.10)
break[](#l2.11)
+ # set the parser name if requested if self.dest is not SUPPRESS: setattr(namespace, self.dest, parser_name) @@ -2307,11 +2313,18 @@ class ArgumentParser(_AttributeHolder, _ def _check_value(self, action, value): # converted value must be one of the choices (if specified)
if action.choices is not None and value not in action.choices:[](#l2.20)
args = {'value': value,[](#l2.21)
'choices': ', '.join(map(repr, action.choices))}[](#l2.22)
msg = _('invalid choice: %(value)r (choose from %(choices)s)')[](#l2.23)
raise ArgumentError(action, msg % args)[](#l2.24)
if action.choices is not None:[](#l2.25)
ac = [ax for ax in action.choices if str(ax).startswith(str(value))][](#l2.26)
if len(ac) == 0:[](#l2.27)
args = {'value': value,[](#l2.28)
'choices': ', '.join(map(repr, action.choices))}[](#l2.29)
msg = _('invalid choice: %(value)r (choose from %(choices)s)')[](#l2.30)
raise ArgumentError(action, msg % args)[](#l2.31)
elif len(ac) > 1:[](#l2.32)
args = {'value': value,[](#l2.33)
'choices': ', '.join(ac)}[](#l2.34)
msg = _('ambiguous choice: %(value)r could match %(choices)s')[](#l2.35)
raise ArgumentError(action, msg % args)[](#l2.36)
# ======================= # Help-formatting methods
--- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -1842,6 +1842,22 @@ class TestAddSubparsers(TestCase): parser3.add_argument('t', type=int, help='t help') parser3.add_argument('u', nargs='...', help='u help')
# add fourth sub-parser (to test abbreviations)[](#l3.7)
parser4_kwargs = dict(description='lost description')[](#l3.8)
if subparser_help:[](#l3.9)
parser4_kwargs['help'] = 'lost help'[](#l3.10)
parser4 = subparsers.add_parser('lost', **parser4_kwargs)[](#l3.11)
parser4.add_argument('-w', type=int, help='w help')[](#l3.12)
parser4.add_argument('x', choices='abc', help='x help')[](#l3.13)
# add fifth sub-parser, with longer name (to test abbreviations)[](#l3.15)
parser5_kwargs = dict(description='long description')[](#l3.16)
if subparser_help:[](#l3.17)
parser5_kwargs['help'] = 'long help'[](#l3.18)
parser5 = subparsers.add_parser('long', **parser5_kwargs)[](#l3.19)
parser5.add_argument('-w', type=int, help='w help')[](#l3.20)
parser5.add_argument('x', choices='abc', help='x help')[](#l3.21)
+ # return the main parser return parser @@ -1857,6 +1873,24 @@ class TestAddSubparsers(TestCase): args = args_str.split() self.assertArgumentParserError(self.parser.parse_args, args)
- def test_parse_args_abbreviation(self):
# check some non-failure cases:[](#l3.31)
self.assertEqual([](#l3.32)
self.parser.parse_args('0.5 long b -w 7'.split()),[](#l3.33)
NS(foo=False, bar=0.5, w=7, x='b'),[](#l3.34)
)[](#l3.35)
self.assertEqual([](#l3.36)
self.parser.parse_args('0.5 lon b -w 7'.split()),[](#l3.37)
NS(foo=False, bar=0.5, w=7, x='b'),[](#l3.38)
)[](#l3.39)
self.assertEqual([](#l3.40)
self.parser.parse_args('0.5 los b -w 7'.split()),[](#l3.41)
NS(foo=False, bar=0.5, w=7, x='b'),[](#l3.42)
)[](#l3.43)
# check a failure case: 'lo' is ambiguous[](#l3.44)
self.assertArgumentParserError(self.parser.parse_args,[](#l3.45)
'0.5 lo b -w 7'.split())[](#l3.46)
+ def test_parse_args(self): # check some non-failure cases: self.assertEqual( @@ -1909,78 +1943,80 @@ class TestAddSubparsers(TestCase): def test_help(self): self.assertEqual(self.parser.format_usage(),
'usage: PROG [-h] [--foo] bar {1,2,3} ...\n')[](#l3.55)
'usage: PROG [-h] [--foo] bar {1,2,3,lost,long} ...\n')[](#l3.56) self.assertEqual(self.parser.format_help(), textwrap.dedent('''\[](#l3.57)
usage: PROG [-h] [--foo] bar {1,2,3} ...[](#l3.58)
usage: PROG [-h] [--foo] bar {1,2,3,lost,long} ...[](#l3.59)
main description positional arguments:
bar bar help[](#l3.64)
{1,2,3} command help[](#l3.65)
bar bar help[](#l3.66)
{1,2,3,lost,long} command help[](#l3.67)
-h, --help show this help message and exit[](#l3.70)
--foo foo help[](#l3.71)
-h, --help show this help message and exit[](#l3.72)
--foo foo help[](#l3.73) '''))[](#l3.74)
def test_help_extra_prefix_chars(self): # Make sure - is still used for help if it is a non-first prefix char parser = self._get_parser(prefix_chars='+:-') self.assertEqual(parser.format_usage(),
'usage: PROG [-h] [++foo] bar {1,2,3} ...\n')[](#l3.80)
'usage: PROG [-h] [++foo] bar {1,2,3,lost,long} ...\n')[](#l3.81) self.assertEqual(parser.format_help(), textwrap.dedent('''\[](#l3.82)
usage: PROG [-h] [++foo] bar {1,2,3} ...[](#l3.83)
usage: PROG [-h] [++foo] bar {1,2,3,lost,long} ...[](#l3.84)
main description positional arguments:
bar bar help[](#l3.89)
{1,2,3} command help[](#l3.90)
bar bar help[](#l3.91)
{1,2,3,lost,long} command help[](#l3.92)
-h, --help show this help message and exit[](#l3.95)
++foo foo help[](#l3.96)
-h, --help show this help message and exit[](#l3.97)
++foo foo help[](#l3.98) '''))[](#l3.99)
def test_help_alternate_prefix_chars(self): parser = self._get_parser(prefix_chars='+:/') self.assertEqual(parser.format_usage(),
'usage: PROG [+h] [++foo] bar {1,2,3} ...\n')[](#l3.105)
'usage: PROG [+h] [++foo] bar {1,2,3,lost,long} ...\n')[](#l3.106) self.assertEqual(parser.format_help(), textwrap.dedent('''\[](#l3.107)
usage: PROG [+h] [++foo] bar {1,2,3} ...[](#l3.108)
usage: PROG [+h] [++foo] bar {1,2,3,lost,long} ...[](#l3.109)
main description positional arguments:
bar bar help[](#l3.114)
{1,2,3} command help[](#l3.115)
bar bar help[](#l3.116)
{1,2,3,lost,long} command help[](#l3.117)
+h, ++help show this help message and exit[](#l3.120)
++foo foo help[](#l3.121)
+h, ++help show this help message and exit[](#l3.122)
++foo foo help[](#l3.123) '''))[](#l3.124)
def test_parser_command_help(self): self.assertEqual(self.command_help_parser.format_usage(),
'usage: PROG [-h] [--foo] bar {1,2,3} ...\n')[](#l3.128)
'usage: PROG [-h] [--foo] bar {1,2,3,lost,long} ...\n')[](#l3.129) self.assertEqual(self.command_help_parser.format_help(),[](#l3.130) textwrap.dedent('''\[](#l3.131)
usage: PROG [-h] [--foo] bar {1,2,3} ...[](#l3.132)
usage: PROG [-h] [--foo] bar {1,2,3,lost,long} ...[](#l3.133)
main description positional arguments:
bar bar help[](#l3.138)
{1,2,3} command help[](#l3.139)
1 1 help[](#l3.140)
2 2 help[](#l3.141)
3 3 help[](#l3.142)
bar bar help[](#l3.143)
{1,2,3,lost,long} command help[](#l3.144)
1 1 help[](#l3.145)
2 2 help[](#l3.146)
3 3 help[](#l3.147)
lost lost help[](#l3.148)
long long help[](#l3.149)
-h, --help show this help message and exit[](#l3.152)
--foo foo help[](#l3.153)
-h, --help show this help message and exit[](#l3.154)
--foo foo help[](#l3.155) '''))[](#l3.156)
def test_subparser_title_help(self): @@ -2083,6 +2119,8 @@ class TestAddSubparsers(TestCase): 1 help 2 2 help 3 3 help
lost lost help[](#l3.163)
long long help[](#l3.164) """))[](#l3.165)