mirror of https://github.com/python/cpython
gh-58282: Fix support of tuple metavar for positional arguments in argparse (GH-124782)
Previously, formatting help output or error message for positional argument with a tuple metavar raised exception. Co-authored-by: Cyker Way <cykerway@gmail.com>
This commit is contained in:
parent
9ce90206b7
commit
9b31a2d83f
|
@ -527,8 +527,7 @@ class HelpFormatter(object):
|
||||||
def _format_action_invocation(self, action):
|
def _format_action_invocation(self, action):
|
||||||
if not action.option_strings:
|
if not action.option_strings:
|
||||||
default = self._get_default_metavar_for_positional(action)
|
default = self._get_default_metavar_for_positional(action)
|
||||||
metavar, = self._metavar_formatter(action, default)(1)
|
return ' '.join(self._metavar_formatter(action, default)(1))
|
||||||
return metavar
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
|
@ -703,7 +702,15 @@ def _get_action_name(argument):
|
||||||
elif argument.option_strings:
|
elif argument.option_strings:
|
||||||
return '/'.join(argument.option_strings)
|
return '/'.join(argument.option_strings)
|
||||||
elif argument.metavar not in (None, SUPPRESS):
|
elif argument.metavar not in (None, SUPPRESS):
|
||||||
return argument.metavar
|
metavar = argument.metavar
|
||||||
|
if not isinstance(metavar, tuple):
|
||||||
|
return metavar
|
||||||
|
if argument.nargs == ZERO_OR_MORE and len(metavar) == 2:
|
||||||
|
return '%s[, %s]' % metavar
|
||||||
|
elif argument.nargs == ONE_OR_MORE:
|
||||||
|
return '%s[, %s]' % metavar
|
||||||
|
else:
|
||||||
|
return ', '.join(metavar)
|
||||||
elif argument.dest not in (None, SUPPRESS):
|
elif argument.dest not in (None, SUPPRESS):
|
||||||
return argument.dest
|
return argument.dest
|
||||||
elif argument.choices:
|
elif argument.choices:
|
||||||
|
|
|
@ -4898,7 +4898,7 @@ class TestHelpNone(HelpTestCase):
|
||||||
version = ''
|
version = ''
|
||||||
|
|
||||||
|
|
||||||
class TestHelpTupleMetavar(HelpTestCase):
|
class TestHelpTupleMetavarOptional(HelpTestCase):
|
||||||
"""Test specifying metavar as a tuple"""
|
"""Test specifying metavar as a tuple"""
|
||||||
|
|
||||||
parser_signature = Sig(prog='PROG')
|
parser_signature = Sig(prog='PROG')
|
||||||
|
@ -4925,6 +4925,34 @@ class TestHelpTupleMetavar(HelpTestCase):
|
||||||
version = ''
|
version = ''
|
||||||
|
|
||||||
|
|
||||||
|
class TestHelpTupleMetavarPositional(HelpTestCase):
|
||||||
|
"""Test specifying metavar on a Positional as a tuple"""
|
||||||
|
|
||||||
|
parser_signature = Sig(prog='PROG')
|
||||||
|
argument_signatures = [
|
||||||
|
Sig('w', help='w help', nargs='+', metavar=('W1', 'W2')),
|
||||||
|
Sig('x', help='x help', nargs='*', metavar=('X1', 'X2')),
|
||||||
|
Sig('y', help='y help', nargs=3, metavar=('Y1', 'Y2', 'Y3')),
|
||||||
|
Sig('z', help='z help', nargs='?', metavar=('Z1',)),
|
||||||
|
]
|
||||||
|
argument_group_signatures = []
|
||||||
|
usage = '''\
|
||||||
|
usage: PROG [-h] W1 [W2 ...] [X1 [X2 ...]] Y1 Y2 Y3 [Z1]
|
||||||
|
'''
|
||||||
|
help = usage + '''\
|
||||||
|
|
||||||
|
positional arguments:
|
||||||
|
W1 W2 w help
|
||||||
|
X1 X2 x help
|
||||||
|
Y1 Y2 Y3 y help
|
||||||
|
Z1 z help
|
||||||
|
|
||||||
|
options:
|
||||||
|
-h, --help show this help message and exit
|
||||||
|
'''
|
||||||
|
version = ''
|
||||||
|
|
||||||
|
|
||||||
class TestHelpRawText(HelpTestCase):
|
class TestHelpRawText(HelpTestCase):
|
||||||
"""Test the RawTextHelpFormatter"""
|
"""Test the RawTextHelpFormatter"""
|
||||||
|
|
||||||
|
@ -6521,6 +6549,27 @@ class TestExitOnError(TestCase):
|
||||||
'the following arguments are required: bar, baz$',
|
'the following arguments are required: bar, baz$',
|
||||||
self.parser.parse_args, [])
|
self.parser.parse_args, [])
|
||||||
|
|
||||||
|
def test_required_args_with_metavar(self):
|
||||||
|
self.parser.add_argument('bar')
|
||||||
|
self.parser.add_argument('baz', metavar='BaZ')
|
||||||
|
self.assertRaisesRegex(argparse.ArgumentError,
|
||||||
|
'the following arguments are required: bar, BaZ$',
|
||||||
|
self.parser.parse_args, [])
|
||||||
|
|
||||||
|
def test_required_args_n(self):
|
||||||
|
self.parser.add_argument('bar')
|
||||||
|
self.parser.add_argument('baz', nargs=3)
|
||||||
|
self.assertRaisesRegex(argparse.ArgumentError,
|
||||||
|
'the following arguments are required: bar, baz$',
|
||||||
|
self.parser.parse_args, [])
|
||||||
|
|
||||||
|
def test_required_args_n_with_metavar(self):
|
||||||
|
self.parser.add_argument('bar')
|
||||||
|
self.parser.add_argument('baz', nargs=3, metavar=('B', 'A', 'Z'))
|
||||||
|
self.assertRaisesRegex(argparse.ArgumentError,
|
||||||
|
'the following arguments are required: bar, B, A, Z$',
|
||||||
|
self.parser.parse_args, [])
|
||||||
|
|
||||||
def test_required_args_optional(self):
|
def test_required_args_optional(self):
|
||||||
self.parser.add_argument('bar')
|
self.parser.add_argument('bar')
|
||||||
self.parser.add_argument('baz', nargs='?')
|
self.parser.add_argument('baz', nargs='?')
|
||||||
|
@ -6535,6 +6584,20 @@ class TestExitOnError(TestCase):
|
||||||
'the following arguments are required: bar$',
|
'the following arguments are required: bar$',
|
||||||
self.parser.parse_args, [])
|
self.parser.parse_args, [])
|
||||||
|
|
||||||
|
def test_required_args_one_or_more(self):
|
||||||
|
self.parser.add_argument('bar')
|
||||||
|
self.parser.add_argument('baz', nargs='+')
|
||||||
|
self.assertRaisesRegex(argparse.ArgumentError,
|
||||||
|
'the following arguments are required: bar, baz$',
|
||||||
|
self.parser.parse_args, [])
|
||||||
|
|
||||||
|
def test_required_args_one_or_more_with_metavar(self):
|
||||||
|
self.parser.add_argument('bar')
|
||||||
|
self.parser.add_argument('baz', nargs='+', metavar=('BaZ1', 'BaZ2'))
|
||||||
|
self.assertRaisesRegex(argparse.ArgumentError,
|
||||||
|
r'the following arguments are required: bar, BaZ1\[, BaZ2]$',
|
||||||
|
self.parser.parse_args, [])
|
||||||
|
|
||||||
def test_required_args_remainder(self):
|
def test_required_args_remainder(self):
|
||||||
self.parser.add_argument('bar')
|
self.parser.add_argument('bar')
|
||||||
self.parser.add_argument('baz', nargs='...')
|
self.parser.add_argument('baz', nargs='...')
|
||||||
|
@ -6550,6 +6613,39 @@ class TestExitOnError(TestCase):
|
||||||
'one of the arguments --bar --baz is required',
|
'one of the arguments --bar --baz is required',
|
||||||
self.parser.parse_args, [])
|
self.parser.parse_args, [])
|
||||||
|
|
||||||
|
def test_conflicting_mutually_exclusive_args_optional_with_metavar(self):
|
||||||
|
group = self.parser.add_mutually_exclusive_group()
|
||||||
|
group.add_argument('--bar')
|
||||||
|
group.add_argument('baz', nargs='?', metavar='BaZ')
|
||||||
|
self.assertRaisesRegex(argparse.ArgumentError,
|
||||||
|
'argument BaZ: not allowed with argument --bar$',
|
||||||
|
self.parser.parse_args, ['--bar', 'a', 'b'])
|
||||||
|
self.assertRaisesRegex(argparse.ArgumentError,
|
||||||
|
'argument --bar: not allowed with argument BaZ$',
|
||||||
|
self.parser.parse_args, ['a', '--bar', 'b'])
|
||||||
|
|
||||||
|
def test_conflicting_mutually_exclusive_args_zero_or_more_with_metavar1(self):
|
||||||
|
group = self.parser.add_mutually_exclusive_group()
|
||||||
|
group.add_argument('--bar')
|
||||||
|
group.add_argument('baz', nargs='*', metavar=('BAZ1',))
|
||||||
|
self.assertRaisesRegex(argparse.ArgumentError,
|
||||||
|
'argument BAZ1: not allowed with argument --bar$',
|
||||||
|
self.parser.parse_args, ['--bar', 'a', 'b'])
|
||||||
|
self.assertRaisesRegex(argparse.ArgumentError,
|
||||||
|
'argument --bar: not allowed with argument BAZ1$',
|
||||||
|
self.parser.parse_args, ['a', '--bar', 'b'])
|
||||||
|
|
||||||
|
def test_conflicting_mutually_exclusive_args_zero_or_more_with_metavar2(self):
|
||||||
|
group = self.parser.add_mutually_exclusive_group()
|
||||||
|
group.add_argument('--bar')
|
||||||
|
group.add_argument('baz', nargs='*', metavar=('BAZ1', 'BAZ2'))
|
||||||
|
self.assertRaisesRegex(argparse.ArgumentError,
|
||||||
|
r'argument BAZ1\[, BAZ2]: not allowed with argument --bar$',
|
||||||
|
self.parser.parse_args, ['--bar', 'a', 'b'])
|
||||||
|
self.assertRaisesRegex(argparse.ArgumentError,
|
||||||
|
r'argument --bar: not allowed with argument BAZ1\[, BAZ2]$',
|
||||||
|
self.parser.parse_args, ['a', '--bar', 'b'])
|
||||||
|
|
||||||
def test_ambiguous_option(self):
|
def test_ambiguous_option(self):
|
||||||
self.parser.add_argument('--foobaz')
|
self.parser.add_argument('--foobaz')
|
||||||
self.parser.add_argument('--fooble', action='store_true')
|
self.parser.add_argument('--fooble', action='store_true')
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Fix :mod:`argparse` metavar processing to allow positional arguments to have a
|
||||||
|
tuple metavar.
|
Loading…
Reference in New Issue