Merge pull request #2423 from fanninpm/update-argparse

Update argparse
This commit is contained in:
Noah
2021-01-28 20:00:51 -06:00
committed by GitHub
3 changed files with 366 additions and 90 deletions

206
Lib/argparse.py vendored
View File

@@ -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

View File

@@ -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.

View File

@@ -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))
);
}
}