gh-126068: Fix exceptions in the argparse module (GH-126069)

* Only error messages for ArgumentError and ArgumentTypeError are now
  translated.
* ArgumentError is now only used for command line errors, not for logical
  errors in the program.
* TypeError is now raised instead of ValueError for some logical errors.
This commit is contained in:
Serhiy Storchaka 2024-10-30 18:14:27 +02:00 committed by GitHub
parent 1f16df4bfe
commit cc9a183993
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 72 additions and 58 deletions

View file

@ -846,7 +846,7 @@ class Action(_AttributeHolder):
return self.option_strings[0]
def __call__(self, parser, namespace, values, option_string=None):
raise NotImplementedError(_('.__call__() not defined'))
raise NotImplementedError('.__call__() not defined')
class BooleanOptionalAction(Action):
@ -1172,11 +1172,10 @@ class _SubParsersAction(Action):
aliases = kwargs.pop('aliases', ())
if name in self._name_parser_map:
raise ArgumentError(self, _('conflicting subparser: %s') % name)
raise ValueError(f'conflicting subparser: {name}')
for alias in aliases:
if alias in self._name_parser_map:
raise ArgumentError(
self, _('conflicting subparser alias: %s') % alias)
raise ValueError(f'conflicting subparser alias: {alias}')
# create a pseudo-action to hold the choice help
if 'help' in kwargs:
@ -1430,8 +1429,8 @@ class _ActionsContainer(object):
chars = self.prefix_chars
if not args or len(args) == 1 and args[0][0] not in chars:
if args and 'dest' in kwargs:
raise ValueError('dest supplied twice for positional argument,'
' did you mean metavar?')
raise TypeError('dest supplied twice for positional argument,'
' did you mean metavar?')
kwargs = self._get_positional_kwargs(*args, **kwargs)
# otherwise, we're adding an optional argument
@ -1450,7 +1449,7 @@ class _ActionsContainer(object):
action_name = kwargs.get('action')
action_class = self._pop_action_class(kwargs)
if not callable(action_class):
raise ValueError('unknown action "%s"' % (action_class,))
raise ValueError('unknown action {action_class!r}')
action = action_class(**kwargs)
# raise an error if action for positional argument does not
@ -1461,11 +1460,11 @@ class _ActionsContainer(object):
# raise an error if the action type is not callable
type_func = self._registry_get('type', action.type, action.type)
if not callable(type_func):
raise ValueError('%r is not callable' % (type_func,))
raise TypeError(f'{type_func!r} is not callable')
if type_func is FileType:
raise ValueError('%r is a FileType class object, instance of it'
' must be passed' % (type_func,))
raise TypeError(f'{type_func!r} is a FileType class object, '
f'instance of it must be passed')
# raise an error if the metavar does not match the type
if hasattr(self, "_get_formatter"):
@ -1518,8 +1517,8 @@ class _ActionsContainer(object):
if group.title in title_group_map:
# This branch could happen if a derived class added
# groups with duplicated titles in __init__
msg = _('cannot merge actions - two groups are named %r')
raise ValueError(msg % (group.title))
msg = f'cannot merge actions - two groups are named {group.title!r}'
raise ValueError(msg)
title_group_map[group.title] = group
# map each action to its group
@ -1560,7 +1559,7 @@ class _ActionsContainer(object):
def _get_positional_kwargs(self, dest, **kwargs):
# make sure required is not specified
if 'required' in kwargs:
msg = _("'required' is an invalid argument for positionals")
msg = "'required' is an invalid argument for positionals"
raise TypeError(msg)
# mark positional arguments as required if at least one is
@ -1581,11 +1580,9 @@ class _ActionsContainer(object):
for option_string in args:
# error on strings that don't start with an appropriate prefix
if not option_string[0] in self.prefix_chars:
args = {'option': option_string,
'prefix_chars': self.prefix_chars}
msg = _('invalid option string %(option)r: '
'must start with a character %(prefix_chars)r')
raise ValueError(msg % args)
raise ValueError(
f'invalid option string {option_string!r}: '
f'must start with a character {self.prefix_chars!r}')
# strings starting with two prefix characters are long options
option_strings.append(option_string)
@ -1601,8 +1598,8 @@ class _ActionsContainer(object):
dest_option_string = option_strings[0]
dest = dest_option_string.lstrip(self.prefix_chars)
if not dest:
msg = _('dest= is required for options like %r')
raise ValueError(msg % option_string)
msg = f'dest= is required for options like {option_string!r}'
raise TypeError(msg)
dest = dest.replace('-', '_')
# return the updated keyword arguments
@ -1618,8 +1615,8 @@ class _ActionsContainer(object):
try:
return getattr(self, handler_func_name)
except AttributeError:
msg = _('invalid conflict_resolution value: %r')
raise ValueError(msg % self.conflict_handler)
msg = f'invalid conflict_resolution value: {self.conflict_handler!r}'
raise ValueError(msg)
def _check_conflict(self, action):
@ -1727,7 +1724,7 @@ class _MutuallyExclusiveGroup(_ArgumentGroup):
def _add_action(self, action):
if action.required:
msg = _('mutually exclusive arguments must be optional')
msg = 'mutually exclusive arguments must be optional'
raise ValueError(msg)
action = self._container._add_action(action)
self._group_actions.append(action)
@ -1871,7 +1868,7 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
# ==================================
def add_subparsers(self, **kwargs):
if self._subparsers is not None:
raise ArgumentError(None, _('cannot have multiple subparser arguments'))
raise ValueError('cannot have multiple subparser arguments')
# add the parser class to the arguments if it's not present
kwargs.setdefault('parser_class', type(self))
@ -2565,8 +2562,7 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
def _get_value(self, action, arg_string):
type_func = self._registry_get('type', action.type, action.type)
if not callable(type_func):
msg = _('%r is not callable')
raise ArgumentError(action, msg % type_func)
raise TypeError(f'{type_func!r} is not callable')
# convert the value to the appropriate type
try: