GH-99749: Add optional feature to suggest correct names (ArgumentParser) (GH-124456)

This commit is contained in:
Savannah Ostrowski 2024-10-17 00:07:37 -07:00 committed by GitHub
parent a5a7f5e16d
commit 624be8699a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 144 additions and 23 deletions

View file

@ -1773,6 +1773,8 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
- 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
- suggest_on_error - Enables suggestions for mistyped argument choices
and subparser names. (default: ``False``)
"""
def __init__(self,
@ -1788,7 +1790,8 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
conflict_handler='error',
add_help=True,
allow_abbrev=True,
exit_on_error=True):
exit_on_error=True,
suggest_on_error=False):
superinit = super(ArgumentParser, self).__init__
superinit(description=description,
@ -1804,6 +1807,7 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
self.add_help = add_help
self.allow_abbrev = allow_abbrev
self.exit_on_error = exit_on_error
self.suggest_on_error = suggest_on_error
add_group = self.add_argument_group
self._positionals = add_group(_('positional arguments'))
@ -2601,14 +2605,27 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
def _check_value(self, action, value):
# converted value must be one of the choices (if specified)
choices = action.choices
if choices is not None:
if isinstance(choices, str):
choices = iter(choices)
if value not in choices:
args = {'value': str(value),
'choices': ', '.join(map(str, action.choices))}
msg = _('invalid choice: %(value)r (choose from %(choices)s)')
raise ArgumentError(action, msg % args)
if choices is None:
return
if isinstance(choices, str):
choices = iter(choices)
if value not in choices:
args = {'value': str(value),
'choices': ', '.join(map(str, action.choices))}
msg = _('invalid choice: %(value)r (choose from %(choices)s)')
if self.suggest_on_error and isinstance(value, str):
if all(isinstance(choice, str) for choice in action.choices):
import difflib
suggestions = difflib.get_close_matches(value, action.choices, 1)
if suggestions:
args['closest'] = suggestions[0]
msg = _('invalid choice: %(value)r, maybe you meant %(closest)r? '
'(choose from %(choices)s)')
raise ArgumentError(action, msg % args)
# =======================
# Help-formatting methods