mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
206
Lib/argparse.py
vendored
206
Lib/argparse.py
vendored
@@ -1,4 +1,5 @@
|
||||
# Author: Steven J. Bethard <steven.bethard@gmail.com>.
|
||||
# New maintainer as of 29 August 2019: Raymond Hettinger <raymond.hettinger@gmail.com>
|
||||
|
||||
"""Command-line parsing library
|
||||
|
||||
@@ -66,6 +67,7 @@ __all__ = [
|
||||
'ArgumentParser',
|
||||
'ArgumentError',
|
||||
'ArgumentTypeError',
|
||||
'BooleanOptionalAction',
|
||||
'FileType',
|
||||
'HelpFormatter',
|
||||
'ArgumentDefaultsHelpFormatter',
|
||||
@@ -127,7 +129,7 @@ class _AttributeHolder(object):
|
||||
return '%s(%s)' % (type_name, ', '.join(arg_strings))
|
||||
|
||||
def _get_kwargs(self):
|
||||
return sorted(self.__dict__.items())
|
||||
return list(self.__dict__.items())
|
||||
|
||||
def _get_args(self):
|
||||
return []
|
||||
@@ -164,15 +166,12 @@ class HelpFormatter(object):
|
||||
|
||||
# default setting for width
|
||||
if width is None:
|
||||
try:
|
||||
width = int(_os.environ['COLUMNS'])
|
||||
except (KeyError, ValueError):
|
||||
width = 80
|
||||
import shutil
|
||||
width = shutil.get_terminal_size().columns
|
||||
width -= 2
|
||||
|
||||
self._prog = prog
|
||||
self._indent_increment = indent_increment
|
||||
self._max_help_position = max_help_position
|
||||
self._max_help_position = min(max_help_position,
|
||||
max(width - 20, indent_increment * 2))
|
||||
self._width = width
|
||||
@@ -265,7 +264,7 @@ class HelpFormatter(object):
|
||||
invocations.append(get_invocation(subaction))
|
||||
|
||||
# update the maximum item length
|
||||
invocation_length = max([len(s) for s in invocations])
|
||||
invocation_length = max(map(len, invocations))
|
||||
action_length = invocation_length + self._current_indent
|
||||
self._action_max_length = max(self._action_max_length,
|
||||
action_length)
|
||||
@@ -407,13 +406,19 @@ class HelpFormatter(object):
|
||||
inserts[start] += ' ['
|
||||
else:
|
||||
inserts[start] = '['
|
||||
inserts[end] = ']'
|
||||
if end in inserts:
|
||||
inserts[end] += ']'
|
||||
else:
|
||||
inserts[end] = ']'
|
||||
else:
|
||||
if start in inserts:
|
||||
inserts[start] += ' ('
|
||||
else:
|
||||
inserts[start] = '('
|
||||
inserts[end] = ')'
|
||||
if end in inserts:
|
||||
inserts[end] += ')'
|
||||
else:
|
||||
inserts[end] = ')'
|
||||
for i in range(start + 1, end):
|
||||
inserts[i] = '|'
|
||||
|
||||
@@ -450,7 +455,7 @@ class HelpFormatter(object):
|
||||
# if the Optional doesn't take a value, format is:
|
||||
# -s or --long
|
||||
if action.nargs == 0:
|
||||
part = '%s' % option_string
|
||||
part = action.format_usage()
|
||||
|
||||
# if the Optional takes a value, format is:
|
||||
# -s ARGS or --long ARGS
|
||||
@@ -586,7 +591,11 @@ class HelpFormatter(object):
|
||||
elif action.nargs == OPTIONAL:
|
||||
result = '[%s]' % get_metavar(1)
|
||||
elif action.nargs == ZERO_OR_MORE:
|
||||
result = '[%s [%s ...]]' % get_metavar(2)
|
||||
metavar = get_metavar(1)
|
||||
if len(metavar) == 2:
|
||||
result = '[%s [%s ...]]' % metavar
|
||||
else:
|
||||
result = '[%s ...]' % metavar
|
||||
elif action.nargs == ONE_OR_MORE:
|
||||
result = '%s [%s ...]' % get_metavar(2)
|
||||
elif action.nargs == REMAINDER:
|
||||
@@ -596,7 +605,10 @@ class HelpFormatter(object):
|
||||
elif action.nargs == SUPPRESS:
|
||||
result = ''
|
||||
else:
|
||||
formats = ['%s' for _ in range(action.nargs)]
|
||||
try:
|
||||
formats = ['%s' for _ in range(action.nargs)]
|
||||
except TypeError:
|
||||
raise ValueError("invalid nargs value") from None
|
||||
result = ' '.join(formats) % get_metavar(action.nargs)
|
||||
return result
|
||||
|
||||
@@ -835,9 +847,52 @@ class Action(_AttributeHolder):
|
||||
]
|
||||
return [(name, getattr(self, name)) for name in names]
|
||||
|
||||
def format_usage(self):
|
||||
return self.option_strings[0]
|
||||
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
raise NotImplementedError(_('.__call__() not defined'))
|
||||
|
||||
class BooleanOptionalAction(Action):
|
||||
def __init__(self,
|
||||
option_strings,
|
||||
dest,
|
||||
default=None,
|
||||
type=None,
|
||||
choices=None,
|
||||
required=False,
|
||||
help=None,
|
||||
metavar=None):
|
||||
|
||||
_option_strings = []
|
||||
for option_string in option_strings:
|
||||
_option_strings.append(option_string)
|
||||
|
||||
if option_string.startswith('--'):
|
||||
option_string = '--no-' + option_string[2:]
|
||||
_option_strings.append(option_string)
|
||||
|
||||
if help is not None and default is not None:
|
||||
help += f" (default: {default})"
|
||||
|
||||
super().__init__(
|
||||
option_strings=_option_strings,
|
||||
dest=dest,
|
||||
nargs=0,
|
||||
default=default,
|
||||
type=type,
|
||||
choices=choices,
|
||||
required=required,
|
||||
help=help,
|
||||
metavar=metavar)
|
||||
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
if option_string in self.option_strings:
|
||||
setattr(namespace, self.dest, not option_string.startswith('--no-'))
|
||||
|
||||
def format_usage(self):
|
||||
return ' | '.join(self.option_strings)
|
||||
|
||||
|
||||
class _StoreAction(Action):
|
||||
|
||||
@@ -853,7 +908,7 @@ class _StoreAction(Action):
|
||||
help=None,
|
||||
metavar=None):
|
||||
if nargs == 0:
|
||||
raise ValueError('nargs for store actions must be > 0; if you '
|
||||
raise ValueError('nargs for store actions must be != 0; if you '
|
||||
'have nothing to store, actions such as store '
|
||||
'true or store const may be more appropriate')
|
||||
if const is not None and nargs != OPTIONAL:
|
||||
@@ -945,7 +1000,7 @@ class _AppendAction(Action):
|
||||
help=None,
|
||||
metavar=None):
|
||||
if nargs == 0:
|
||||
raise ValueError('nargs for append actions must be > 0; if arg '
|
||||
raise ValueError('nargs for append actions must be != 0; if arg '
|
||||
'strings are not supplying the value to append, '
|
||||
'the append const action may be more appropriate')
|
||||
if const is not None and nargs != OPTIONAL:
|
||||
@@ -1157,6 +1212,12 @@ class _SubParsersAction(Action):
|
||||
vars(namespace).setdefault(_UNRECOGNIZED_ARGS_ATTR, [])
|
||||
getattr(namespace, _UNRECOGNIZED_ARGS_ATTR).extend(arg_strings)
|
||||
|
||||
class _ExtendAction(_AppendAction):
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
items = getattr(namespace, self.dest, None)
|
||||
items = _copy_items(items)
|
||||
items.extend(values)
|
||||
setattr(namespace, self.dest, items)
|
||||
|
||||
# ==============
|
||||
# Type classes
|
||||
@@ -1201,8 +1262,9 @@ class FileType(object):
|
||||
return open(string, self._mode, self._bufsize, self._encoding,
|
||||
self._errors)
|
||||
except OSError as e:
|
||||
message = _("can't open '%s': %s")
|
||||
raise ArgumentTypeError(message % (string, e))
|
||||
args = {'filename': string, 'error': e}
|
||||
message = _("can't open '%(filename)s': %(error)s")
|
||||
raise ArgumentTypeError(message % args)
|
||||
|
||||
def __repr__(self):
|
||||
args = self._mode, self._bufsize
|
||||
@@ -1265,6 +1327,7 @@ class _ActionsContainer(object):
|
||||
self.register('action', 'help', _HelpAction)
|
||||
self.register('action', 'version', _VersionAction)
|
||||
self.register('action', 'parsers', _SubParsersAction)
|
||||
self.register('action', 'extend', _ExtendAction)
|
||||
|
||||
# raise an exception if the conflict handler is invalid
|
||||
self._get_handler()
|
||||
@@ -1357,6 +1420,10 @@ class _ActionsContainer(object):
|
||||
if not callable(type_func):
|
||||
raise ValueError('%r is not callable' % (type_func,))
|
||||
|
||||
if type_func is FileType:
|
||||
raise ValueError('%r is a FileType class object, instance of it'
|
||||
' must be passed' % (type_func,))
|
||||
|
||||
# raise an error if the metavar does not match the type
|
||||
if hasattr(self, "_get_formatter"):
|
||||
try:
|
||||
@@ -1471,10 +1538,8 @@ class _ActionsContainer(object):
|
||||
|
||||
# strings starting with two prefix characters are long options
|
||||
option_strings.append(option_string)
|
||||
if option_string[0] in self.prefix_chars:
|
||||
if len(option_string) > 1:
|
||||
if option_string[1] in self.prefix_chars:
|
||||
long_option_strings.append(option_string)
|
||||
if len(option_string) > 1 and option_string[1] in self.prefix_chars:
|
||||
long_option_strings.append(option_string)
|
||||
|
||||
# infer destination, '--foo-bar' -> 'foo_bar' and '-x' -> 'x'
|
||||
dest = kwargs.pop('dest', None)
|
||||
@@ -1614,6 +1679,8 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
|
||||
- conflict_handler -- String indicating how to handle conflicts
|
||||
- add_help -- Add a -h/-help option
|
||||
- allow_abbrev -- Allow long options to be abbreviated unambiguously
|
||||
- exit_on_error -- Determines whether or not ArgumentParser exits with
|
||||
error info when an error occurs
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
@@ -1628,19 +1695,14 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
|
||||
argument_default=None,
|
||||
conflict_handler='error',
|
||||
add_help=True,
|
||||
allow_abbrev=True):
|
||||
_ActionsContainer.__init__(self,
|
||||
description=description,
|
||||
prefix_chars=prefix_chars,
|
||||
argument_default=argument_default,
|
||||
conflict_handler=conflict_handler)
|
||||
# FIXME: get multiple inheritance method resolution right so we can use
|
||||
# what's below instead of the modified version above
|
||||
# superinit = super(ArgumentParser, self).__init__
|
||||
# superinit(description=description,
|
||||
# prefix_chars=prefix_chars,
|
||||
# argument_default=argument_default,
|
||||
# conflict_handler=conflict_handler)
|
||||
allow_abbrev=True,
|
||||
exit_on_error=True):
|
||||
|
||||
superinit = super(ArgumentParser, self).__init__
|
||||
superinit(description=description,
|
||||
prefix_chars=prefix_chars,
|
||||
argument_default=argument_default,
|
||||
conflict_handler=conflict_handler)
|
||||
|
||||
# default setting for prog
|
||||
if prog is None:
|
||||
@@ -1653,6 +1715,7 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
|
||||
self.fromfile_prefix_chars = fromfile_prefix_chars
|
||||
self.add_help = add_help
|
||||
self.allow_abbrev = allow_abbrev
|
||||
self.exit_on_error = exit_on_error
|
||||
|
||||
add_group = self.add_argument_group
|
||||
self._positionals = add_group(_('positional arguments'))
|
||||
@@ -1783,15 +1846,19 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
|
||||
setattr(namespace, dest, self._defaults[dest])
|
||||
|
||||
# parse the arguments and exit if there are any errors
|
||||
try:
|
||||
if self.exit_on_error:
|
||||
try:
|
||||
namespace, args = self._parse_known_args(args, namespace)
|
||||
except ArgumentError:
|
||||
err = _sys.exc_info()[1]
|
||||
self.error(str(err))
|
||||
else:
|
||||
namespace, args = self._parse_known_args(args, namespace)
|
||||
if hasattr(namespace, _UNRECOGNIZED_ARGS_ATTR):
|
||||
args.extend(getattr(namespace, _UNRECOGNIZED_ARGS_ATTR))
|
||||
delattr(namespace, _UNRECOGNIZED_ARGS_ATTR)
|
||||
return namespace, args
|
||||
except ArgumentError:
|
||||
err = _sys.exc_info()[1]
|
||||
self.error(str(err))
|
||||
|
||||
if hasattr(namespace, _UNRECOGNIZED_ARGS_ATTR):
|
||||
args.extend(getattr(namespace, _UNRECOGNIZED_ARGS_ATTR))
|
||||
delattr(namespace, _UNRECOGNIZED_ARGS_ATTR)
|
||||
return namespace, args
|
||||
|
||||
def _parse_known_args(self, arg_strings, namespace):
|
||||
# replace arg strings that are file references
|
||||
@@ -2080,10 +2147,11 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
|
||||
OPTIONAL: _('expected at most one argument'),
|
||||
ONE_OR_MORE: _('expected at least one argument'),
|
||||
}
|
||||
default = ngettext('expected %s argument',
|
||||
msg = nargs_errors.get(action.nargs)
|
||||
if msg is None:
|
||||
msg = ngettext('expected %s argument',
|
||||
'expected %s arguments',
|
||||
action.nargs) % action.nargs
|
||||
msg = nargs_errors.get(action.nargs, default)
|
||||
raise ArgumentError(action, msg)
|
||||
|
||||
# return the number of arguments matched
|
||||
@@ -2130,24 +2198,23 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
|
||||
action = self._option_string_actions[option_string]
|
||||
return action, option_string, explicit_arg
|
||||
|
||||
if self.allow_abbrev:
|
||||
# search through all possible prefixes of the option string
|
||||
# and all actions in the parser for possible interpretations
|
||||
option_tuples = self._get_option_tuples(arg_string)
|
||||
# search through all possible prefixes of the option string
|
||||
# and all actions in the parser for possible interpretations
|
||||
option_tuples = self._get_option_tuples(arg_string)
|
||||
|
||||
# if multiple actions match, the option string was ambiguous
|
||||
if len(option_tuples) > 1:
|
||||
options = ', '.join([option_string
|
||||
for action, option_string, explicit_arg in option_tuples])
|
||||
args = {'option': arg_string, 'matches': options}
|
||||
msg = _('ambiguous option: %(option)s could match %(matches)s')
|
||||
self.error(msg % args)
|
||||
# if multiple actions match, the option string was ambiguous
|
||||
if len(option_tuples) > 1:
|
||||
options = ', '.join([option_string
|
||||
for action, option_string, explicit_arg in option_tuples])
|
||||
args = {'option': arg_string, 'matches': options}
|
||||
msg = _('ambiguous option: %(option)s could match %(matches)s')
|
||||
self.error(msg % args)
|
||||
|
||||
# if exactly one action matched, this segmentation is good,
|
||||
# so return the parsed action
|
||||
elif len(option_tuples) == 1:
|
||||
option_tuple, = option_tuples
|
||||
return option_tuple
|
||||
# if exactly one action matched, this segmentation is good,
|
||||
# so return the parsed action
|
||||
elif len(option_tuples) == 1:
|
||||
option_tuple, = option_tuples
|
||||
return option_tuple
|
||||
|
||||
# if it was not found as an option, but it looks like a negative
|
||||
# number, it was meant to be positional
|
||||
@@ -2171,16 +2238,17 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
|
||||
# split at the '='
|
||||
chars = self.prefix_chars
|
||||
if option_string[0] in chars and option_string[1] in chars:
|
||||
if '=' in option_string:
|
||||
option_prefix, explicit_arg = option_string.split('=', 1)
|
||||
else:
|
||||
option_prefix = option_string
|
||||
explicit_arg = None
|
||||
for option_string in self._option_string_actions:
|
||||
if option_string.startswith(option_prefix):
|
||||
action = self._option_string_actions[option_string]
|
||||
tup = action, option_string, explicit_arg
|
||||
result.append(tup)
|
||||
if self.allow_abbrev:
|
||||
if '=' in option_string:
|
||||
option_prefix, explicit_arg = option_string.split('=', 1)
|
||||
else:
|
||||
option_prefix = option_string
|
||||
explicit_arg = None
|
||||
for option_string in self._option_string_actions:
|
||||
if option_string.startswith(option_prefix):
|
||||
action = self._option_string_actions[option_string]
|
||||
tup = action, option_string, explicit_arg
|
||||
result.append(tup)
|
||||
|
||||
# single character options can be concatenated with their arguments
|
||||
# but multiple character options always have to have their argument
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# Author: Steven J. Bethard <steven.bethard@gmail.com>.
|
||||
|
||||
import codecs
|
||||
import inspect
|
||||
import os
|
||||
import shutil
|
||||
@@ -106,7 +105,8 @@ def stderr_to_parser_error(parse_args, *args, **kwargs):
|
||||
code = sys.exc_info()[1].code
|
||||
stdout = sys.stdout.getvalue()
|
||||
stderr = sys.stderr.getvalue()
|
||||
raise ArgumentParserError("SystemExit", stdout, stderr, code)
|
||||
raise ArgumentParserError(
|
||||
"SystemExit", stdout, stderr, code) from None
|
||||
finally:
|
||||
sys.stdout = old_stdout
|
||||
sys.stderr = old_stderr
|
||||
@@ -687,6 +687,38 @@ class TestOptionalsActionStoreTrue(ParserTestCase):
|
||||
('--apple', NS(apple=True)),
|
||||
]
|
||||
|
||||
class TestBooleanOptionalAction(ParserTestCase):
|
||||
"""Tests BooleanOptionalAction"""
|
||||
|
||||
argument_signatures = [Sig('--foo', action=argparse.BooleanOptionalAction)]
|
||||
failures = ['--foo bar', '--foo=bar']
|
||||
successes = [
|
||||
('', NS(foo=None)),
|
||||
('--foo', NS(foo=True)),
|
||||
('--no-foo', NS(foo=False)),
|
||||
('--foo --no-foo', NS(foo=False)), # useful for aliases
|
||||
('--no-foo --foo', NS(foo=True)),
|
||||
]
|
||||
|
||||
def test_const(self):
|
||||
# See bpo-40862
|
||||
parser = argparse.ArgumentParser()
|
||||
with self.assertRaises(TypeError) as cm:
|
||||
parser.add_argument('--foo', const=True, action=argparse.BooleanOptionalAction)
|
||||
|
||||
self.assertIn("got an unexpected keyword argument 'const'", str(cm.exception))
|
||||
|
||||
class TestBooleanOptionalActionRequired(ParserTestCase):
|
||||
"""Tests BooleanOptionalAction required"""
|
||||
|
||||
argument_signatures = [
|
||||
Sig('--foo', required=True, action=argparse.BooleanOptionalAction)
|
||||
]
|
||||
failures = ['']
|
||||
successes = [
|
||||
('--foo', NS(foo=True)),
|
||||
('--no-foo', NS(foo=False)),
|
||||
]
|
||||
|
||||
class TestOptionalsActionAppend(ParserTestCase):
|
||||
"""Tests the append action for an Optional"""
|
||||
@@ -786,6 +818,62 @@ class TestOptionalsDisallowLongAbbreviation(ParserTestCase):
|
||||
('--foonly 7 --foodle --foo 2', NS(foo='2', foodle=True, foonly='7')),
|
||||
]
|
||||
|
||||
|
||||
class TestOptionalsDisallowLongAbbreviationPrefixChars(ParserTestCase):
|
||||
"""Disallowing abbreviations works with alternative prefix characters"""
|
||||
|
||||
parser_signature = Sig(prefix_chars='+', allow_abbrev=False)
|
||||
argument_signatures = [
|
||||
Sig('++foo'),
|
||||
Sig('++foodle', action='store_true'),
|
||||
Sig('++foonly'),
|
||||
]
|
||||
failures = ['+foon 3', '++foon 3', '++food', '++food ++foo 2']
|
||||
successes = [
|
||||
('', NS(foo=None, foodle=False, foonly=None)),
|
||||
('++foo 3', NS(foo='3', foodle=False, foonly=None)),
|
||||
('++foonly 7 ++foodle ++foo 2', NS(foo='2', foodle=True, foonly='7')),
|
||||
]
|
||||
|
||||
|
||||
class TestDisallowLongAbbreviationAllowsShortGrouping(ParserTestCase):
|
||||
"""Do not allow abbreviations of long options at all"""
|
||||
|
||||
parser_signature = Sig(allow_abbrev=False)
|
||||
argument_signatures = [
|
||||
Sig('-r'),
|
||||
Sig('-c', action='count'),
|
||||
]
|
||||
failures = ['-r', '-c -r']
|
||||
successes = [
|
||||
('', NS(r=None, c=None)),
|
||||
('-ra', NS(r='a', c=None)),
|
||||
('-rcc', NS(r='cc', c=None)),
|
||||
('-cc', NS(r=None, c=2)),
|
||||
('-cc -ra', NS(r='a', c=2)),
|
||||
('-ccrcc', NS(r='cc', c=2)),
|
||||
]
|
||||
|
||||
|
||||
class TestDisallowLongAbbreviationAllowsShortGroupingPrefix(ParserTestCase):
|
||||
"""Short option grouping works with custom prefix and allow_abbrev=False"""
|
||||
|
||||
parser_signature = Sig(prefix_chars='+', allow_abbrev=False)
|
||||
argument_signatures = [
|
||||
Sig('+r'),
|
||||
Sig('+c', action='count'),
|
||||
]
|
||||
failures = ['+r', '+c +r']
|
||||
successes = [
|
||||
('', NS(r=None, c=None)),
|
||||
('+ra', NS(r='a', c=None)),
|
||||
('+rcc', NS(r='cc', c=None)),
|
||||
('+cc', NS(r=None, c=2)),
|
||||
('+cc +ra', NS(r='a', c=2)),
|
||||
('+ccrcc', NS(r='cc', c=2)),
|
||||
]
|
||||
|
||||
|
||||
# ================
|
||||
# Positional tests
|
||||
# ================
|
||||
@@ -1619,6 +1707,24 @@ class TestFileTypeOpenArgs(TestCase):
|
||||
m.assert_called_with('foo', *args)
|
||||
|
||||
|
||||
class TestFileTypeMissingInitialization(TestCase):
|
||||
"""
|
||||
Test that add_argument throws an error if FileType class
|
||||
object was passed instead of instance of FileType
|
||||
"""
|
||||
|
||||
def test(self):
|
||||
parser = argparse.ArgumentParser()
|
||||
with self.assertRaises(ValueError) as cm:
|
||||
parser.add_argument('-x', type=argparse.FileType)
|
||||
|
||||
self.assertEqual(
|
||||
'%r is a FileType class object, instance of it must be passed'
|
||||
% (argparse.FileType,),
|
||||
str(cm.exception)
|
||||
)
|
||||
|
||||
|
||||
class TestTypeCallable(ParserTestCase):
|
||||
"""Test some callables as option/argument types"""
|
||||
|
||||
@@ -1786,6 +1892,15 @@ class TestActionRegistration(TestCase):
|
||||
self.assertEqual(parser.parse_args(['42']), NS(badger='foo[42]'))
|
||||
|
||||
|
||||
class TestActionExtend(ParserTestCase):
|
||||
argument_signatures = [
|
||||
Sig('--foo', action="extend", nargs="+", type=str),
|
||||
]
|
||||
failures = ()
|
||||
successes = [
|
||||
('--foo f1 --foo f2 f3 f4', NS(foo=['f1', 'f2', 'f3', 'f4'])),
|
||||
]
|
||||
|
||||
# ================
|
||||
# Subparsers tests
|
||||
# ================
|
||||
@@ -2094,7 +2209,7 @@ class TestAddSubparsers(TestCase):
|
||||
|
||||
def test_subparser2_help(self):
|
||||
self._test_subparser_help('5.0 2 -h', textwrap.dedent('''\
|
||||
usage: PROG bar 2 [-h] [-y {1,2,3}] [z [z ...]]
|
||||
usage: PROG bar 2 [-h] [-y {1,2,3}] [z ...]
|
||||
|
||||
2 description
|
||||
|
||||
@@ -2628,10 +2743,10 @@ class TestMutuallyExclusiveOptionalAndPositional(MEMixin, TestCase):
|
||||
]
|
||||
|
||||
usage_when_not_required = '''\
|
||||
usage: PROG [-h] [--foo | --spam SPAM | badger [badger ...]]
|
||||
usage: PROG [-h] [--foo | --spam SPAM | badger ...]
|
||||
'''
|
||||
usage_when_required = '''\
|
||||
usage: PROG [-h] (--foo | --spam SPAM | badger [badger ...])
|
||||
usage: PROG [-h] (--foo | --spam SPAM | badger ...)
|
||||
'''
|
||||
help = '''\
|
||||
|
||||
@@ -2768,6 +2883,46 @@ class TestMutuallyExclusiveOptionalsAndPositionalsMixed(MEMixin, TestCase):
|
||||
-c c help
|
||||
'''
|
||||
|
||||
class TestMutuallyExclusiveNested(MEMixin, TestCase):
|
||||
|
||||
def get_parser(self, required):
|
||||
parser = ErrorRaisingArgumentParser(prog='PROG')
|
||||
group = parser.add_mutually_exclusive_group(required=required)
|
||||
group.add_argument('-a')
|
||||
group.add_argument('-b')
|
||||
group2 = group.add_mutually_exclusive_group(required=required)
|
||||
group2.add_argument('-c')
|
||||
group2.add_argument('-d')
|
||||
group3 = group2.add_mutually_exclusive_group(required=required)
|
||||
group3.add_argument('-e')
|
||||
group3.add_argument('-f')
|
||||
return parser
|
||||
|
||||
usage_when_not_required = '''\
|
||||
usage: PROG [-h] [-a A | -b B | [-c C | -d D | [-e E | -f F]]]
|
||||
'''
|
||||
usage_when_required = '''\
|
||||
usage: PROG [-h] (-a A | -b B | (-c C | -d D | (-e E | -f F)))
|
||||
'''
|
||||
|
||||
help = '''\
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-a A
|
||||
-b B
|
||||
-c C
|
||||
-d D
|
||||
-e E
|
||||
-f F
|
||||
'''
|
||||
|
||||
# We are only interested in testing the behavior of format_usage().
|
||||
test_failures_when_not_required = None
|
||||
test_failures_when_required = None
|
||||
test_successes_when_not_required = None
|
||||
test_successes_when_required = None
|
||||
|
||||
# =================================================
|
||||
# Mutually exclusive group in parent parser tests
|
||||
# =================================================
|
||||
@@ -3371,6 +3526,10 @@ class TestHelpUsage(HelpTestCase):
|
||||
Sig('a', help='a'),
|
||||
Sig('b', help='b', nargs=2),
|
||||
Sig('c', help='c', nargs='?'),
|
||||
Sig('--foo', help='Whether to foo', action=argparse.BooleanOptionalAction),
|
||||
Sig('--bar', help='Whether to bar', default=True,
|
||||
action=argparse.BooleanOptionalAction),
|
||||
Sig('-f', '--foobar', '--barfoo', action=argparse.BooleanOptionalAction),
|
||||
]
|
||||
argument_group_signatures = [
|
||||
(Sig('group'), [
|
||||
@@ -3381,26 +3540,32 @@ class TestHelpUsage(HelpTestCase):
|
||||
])
|
||||
]
|
||||
usage = '''\
|
||||
usage: PROG [-h] [-w W [W ...]] [-x [X [X ...]]] [-y [Y]] [-z Z Z Z]
|
||||
a b b [c] [d [d ...]] e [e ...]
|
||||
usage: PROG [-h] [-w W [W ...]] [-x [X ...]] [--foo | --no-foo]
|
||||
[--bar | --no-bar]
|
||||
[-f | --foobar | --no-foobar | --barfoo | --no-barfoo] [-y [Y]]
|
||||
[-z Z Z Z]
|
||||
a b b [c] [d ...] e [e ...]
|
||||
'''
|
||||
help = usage + '''\
|
||||
|
||||
positional arguments:
|
||||
a a
|
||||
b b
|
||||
c c
|
||||
a a
|
||||
b b
|
||||
c c
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-w W [W ...] w
|
||||
-x [X [X ...]] x
|
||||
-h, --help show this help message and exit
|
||||
-w W [W ...] w
|
||||
-x [X ...] x
|
||||
--foo, --no-foo Whether to foo
|
||||
--bar, --no-bar Whether to bar (default: True)
|
||||
-f, --foobar, --no-foobar, --barfoo, --no-barfoo
|
||||
|
||||
group:
|
||||
-y [Y] y
|
||||
-z Z Z Z z
|
||||
d d
|
||||
e e
|
||||
-y [Y] y
|
||||
-z Z Z Z z
|
||||
d d
|
||||
e e
|
||||
'''
|
||||
version = ''
|
||||
|
||||
@@ -4218,7 +4383,6 @@ class TestHelpSubparsersWithHelpOrdering(HelpTestCase):
|
||||
|
||||
|
||||
class TestHelpMetavarTypeFormatter(HelpTestCase):
|
||||
""""""
|
||||
|
||||
def custom_type(string):
|
||||
return string
|
||||
@@ -4569,7 +4733,7 @@ class TestStrings(TestCase):
|
||||
|
||||
def test_namespace(self):
|
||||
ns = argparse.Namespace(foo=42, bar='spam')
|
||||
string = "Namespace(bar='spam', foo=42)"
|
||||
string = "Namespace(foo=42, bar='spam')"
|
||||
self.assertStringEqual(ns, string)
|
||||
|
||||
def test_namespace_starkwargs_notidentifier(self):
|
||||
@@ -4995,7 +5159,7 @@ class TestAddArgumentMetavar(TestCase):
|
||||
self.do_test_exception(nargs="*", metavar=tuple())
|
||||
|
||||
def test_nargs_zeroormore_metavar_length1(self):
|
||||
self.do_test_exception(nargs="*", metavar=("1",))
|
||||
self.do_test_no_exception(nargs="*", metavar=("1",))
|
||||
|
||||
def test_nargs_zeroormore_metavar_length2(self):
|
||||
self.do_test_no_exception(nargs="*", metavar=("1", "2"))
|
||||
@@ -5105,6 +5269,35 @@ class TestAddArgumentMetavar(TestCase):
|
||||
def test_nargs_3_metavar_length3(self):
|
||||
self.do_test_no_exception(nargs=3, metavar=("1", "2", "3"))
|
||||
|
||||
|
||||
class TestInvalidNargs(TestCase):
|
||||
|
||||
EXPECTED_INVALID_MESSAGE = "invalid nargs value"
|
||||
EXPECTED_RANGE_MESSAGE = ("nargs for store actions must be != 0; if you "
|
||||
"have nothing to store, actions such as store "
|
||||
"true or store const may be more appropriate")
|
||||
|
||||
def do_test_range_exception(self, nargs):
|
||||
parser = argparse.ArgumentParser()
|
||||
with self.assertRaises(ValueError) as cm:
|
||||
parser.add_argument("--foo", nargs=nargs)
|
||||
self.assertEqual(cm.exception.args[0], self.EXPECTED_RANGE_MESSAGE)
|
||||
|
||||
def do_test_invalid_exception(self, nargs):
|
||||
parser = argparse.ArgumentParser()
|
||||
with self.assertRaises(ValueError) as cm:
|
||||
parser.add_argument("--foo", nargs=nargs)
|
||||
self.assertEqual(cm.exception.args[0], self.EXPECTED_INVALID_MESSAGE)
|
||||
|
||||
# Unit tests for different values of nargs
|
||||
|
||||
def test_nargs_alphabetic(self):
|
||||
self.do_test_invalid_exception(nargs='a')
|
||||
self.do_test_invalid_exception(nargs="abcd")
|
||||
|
||||
def test_nargs_zero(self):
|
||||
self.do_test_range_exception(nargs=0)
|
||||
|
||||
# ============================
|
||||
# from argparse import * tests
|
||||
# ============================
|
||||
@@ -5149,6 +5342,21 @@ class TestWrappingMetavar(TestCase):
|
||||
'''))
|
||||
|
||||
|
||||
class TestExitOnError(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.parser = argparse.ArgumentParser(exit_on_error=False)
|
||||
self.parser.add_argument('--integers', metavar='N', type=int)
|
||||
|
||||
def test_exit_on_error_with_good_args(self):
|
||||
ns = self.parser.parse_args('--integers 4'.split())
|
||||
self.assertEqual(ns, argparse.Namespace(integers=4))
|
||||
|
||||
def test_exit_on_error_with_bad_args(self):
|
||||
with self.assertRaises(argparse.ArgumentError):
|
||||
self.parser.parse_args('--integers a'.split())
|
||||
|
||||
|
||||
def test_main():
|
||||
support.run_unittest(__name__)
|
||||
# Remove global references to avoid looking like we have refleaks.
|
||||
|
||||
@@ -149,7 +149,7 @@ impl PyFunction {
|
||||
kwargs.set_item(name, value, vm)?;
|
||||
} else {
|
||||
return Err(
|
||||
vm.new_type_error(format!("Got an unexpected keyword argument '{}'", name))
|
||||
vm.new_type_error(format!("got an unexpected keyword argument '{}'", name))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user