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

@ -2253,6 +2253,95 @@ class TestNegativeNumber(ParserTestCase):
('--complex -1e-3j', NS(int=None, float=None, complex=-0.001j)),
]
class TestArgumentAndSubparserSuggestions(TestCase):
"""Test error handling and suggestion when a user makes a typo"""
def test_wrong_argument_error_with_suggestions(self):
parser = ErrorRaisingArgumentParser(suggest_on_error=True)
parser.add_argument('foo', choices=['bar', 'baz'])
with self.assertRaises(ArgumentParserError) as excinfo:
parser.parse_args(('bazz',))
self.assertIn(
"error: argument foo: invalid choice: 'bazz', maybe you meant 'baz'? (choose from bar, baz)",
excinfo.exception.stderr
)
def test_wrong_argument_error_no_suggestions(self):
parser = ErrorRaisingArgumentParser(suggest_on_error=False)
parser.add_argument('foo', choices=['bar', 'baz'])
with self.assertRaises(ArgumentParserError) as excinfo:
parser.parse_args(('bazz',))
self.assertIn(
"error: argument foo: invalid choice: 'bazz' (choose from bar, baz)",
excinfo.exception.stderr,
)
def test_wrong_argument_subparsers_with_suggestions(self):
parser = ErrorRaisingArgumentParser(suggest_on_error=True)
subparsers = parser.add_subparsers(required=True)
subparsers.add_parser('foo')
subparsers.add_parser('bar')
with self.assertRaises(ArgumentParserError) as excinfo:
parser.parse_args(('baz',))
self.assertIn(
"error: argument {foo,bar}: invalid choice: 'baz', maybe you meant"
" 'bar'? (choose from foo, bar)",
excinfo.exception.stderr,
)
def test_wrong_argument_subparsers_no_suggestions(self):
parser = ErrorRaisingArgumentParser(suggest_on_error=False)
subparsers = parser.add_subparsers(required=True)
subparsers.add_parser('foo')
subparsers.add_parser('bar')
with self.assertRaises(ArgumentParserError) as excinfo:
parser.parse_args(('baz',))
self.assertIn(
"error: argument {foo,bar}: invalid choice: 'baz' (choose from foo, bar)",
excinfo.exception.stderr,
)
def test_wrong_argument_no_suggestion_implicit(self):
parser = ErrorRaisingArgumentParser()
parser.add_argument('foo', choices=['bar', 'baz'])
with self.assertRaises(ArgumentParserError) as excinfo:
parser.parse_args(('bazz',))
self.assertIn(
"error: argument foo: invalid choice: 'bazz' (choose from bar, baz)",
excinfo.exception.stderr,
)
def test_suggestions_choices_empty(self):
parser = ErrorRaisingArgumentParser(suggest_on_error=True)
parser.add_argument('foo', choices=[])
with self.assertRaises(ArgumentParserError) as excinfo:
parser.parse_args(('bazz',))
self.assertIn(
"error: argument foo: invalid choice: 'bazz' (choose from )",
excinfo.exception.stderr,
)
def test_suggestions_choices_int(self):
parser = ErrorRaisingArgumentParser(suggest_on_error=True)
parser.add_argument('foo', choices=[1, 2])
with self.assertRaises(ArgumentParserError) as excinfo:
parser.parse_args(('3',))
self.assertIn(
"error: argument foo: invalid choice: '3' (choose from 1, 2)",
excinfo.exception.stderr,
)
def test_suggestions_choices_mixed_types(self):
parser = ErrorRaisingArgumentParser(suggest_on_error=True)
parser.add_argument('foo', choices=[1, '2'])
with self.assertRaises(ArgumentParserError) as excinfo:
parser.parse_args(('3',))
self.assertIn(
"error: argument foo: invalid choice: '3' (choose from 1, 2)",
excinfo.exception.stderr,
)
class TestInvalidAction(TestCase):
"""Test invalid user defined Action"""
@ -2505,18 +2594,6 @@ class TestAddSubparsers(TestCase):
'error: the following arguments are required: {foo,bar}\n$'
)
def test_wrong_argument_subparsers_no_destination_error(self):
parser = ErrorRaisingArgumentParser()
subparsers = parser.add_subparsers(required=True)
subparsers.add_parser('foo')
subparsers.add_parser('bar')
with self.assertRaises(ArgumentParserError) as excinfo:
parser.parse_args(('baz',))
self.assertRegex(
excinfo.exception.stderr,
r"error: argument {foo,bar}: invalid choice: 'baz' \(choose from foo, bar\)\n$"
)
def test_optional_subparsers(self):
parser = ErrorRaisingArgumentParser()
subparsers = parser.add_subparsers(dest='command', required=False)
@ -2862,7 +2939,7 @@ class TestParentParsers(TestCase):
parser = ErrorRaisingArgumentParser(parents=[self.ab_mutex_parent])
self._test_mutex_ab(parser.parse_args)
def test_single_granparent_mutex(self):
def test_single_grandparent_mutex(self):
parents = [self.ab_mutex_parent]
parser = ErrorRaisingArgumentParser(add_help=False, parents=parents)
parser = ErrorRaisingArgumentParser(parents=[parser])