-
-
Notifications
You must be signed in to change notification settings - Fork 32.5k
gh-99749: Add closest choice if exists in Argparser if wrong choice picked #99773
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 21 commits
ef8e4fc
7e8dcf8
8c754cd
4fb8ce6
2f197b3
940a66e
badc5ed
2588ef1
ee05c1e
4a36406
fe169bc
9fe0d95
d12e1c4
35f0961
087895c
6eeae5c
9965694
4ef218a
e63a01c
bfc9262
b3b4a9e
b8bc465
ade070f
3753a0d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -84,7 +84,7 @@ | |||||||||
'ZERO_OR_MORE', | ||||||||||
] | ||||||||||
|
||||||||||
|
||||||||||
import difflib as _difflib | ||||||||||
import os as _os | ||||||||||
import re as _re | ||||||||||
import sys as _sys | ||||||||||
|
@@ -2543,9 +2543,18 @@ def _get_value(self, action, arg_string): | |||||||||
def _check_value(self, action, value): | ||||||||||
# converted value must be one of the choices (if specified) | ||||||||||
if action.choices is not None and value not in action.choices: | ||||||||||
args = {'value': value, | ||||||||||
'choices': ', '.join(map(repr, action.choices))} | ||||||||||
msg = _('invalid choice: %(value)r (choose from %(choices)s)') | ||||||||||
closest_choice = _difflib.get_close_matches(value, action.choices) | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this might fail if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. argparser does not accept choices other than string, it will throw error even before reaching at this line There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The second example for the documentation (https://docs.python.org/3/library/argparse.html#choices) parser = argparse.ArgumentParser(prog='doors.py')
parser.add_argument('door', type=int, choices=range(1, 4))
print(parser.parse_args(['3'])) Also - I would use
Suggested change
|
||||||||||
args = { | ||||||||||
'value': value, | ||||||||||
'choices': ', '.join(map(repr, action.choices)), | ||||||||||
} | ||||||||||
if closest_choice := closest_choice and closest_choice[0] or None: | ||||||||||
args['closest'] = closest_choice | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why not the above one liner? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the one liner is less readable - at least to me |
||||||||||
msg = _('invalid choice: %(value)r, maybe you meant %(closest)r? ' | ||||||||||
'(choose from %(choices)s)') | ||||||||||
else: | ||||||||||
msg = _('invalid choice: %(value)r (choose from %(choices)s)') | ||||||||||
|
||||||||||
raise ArgumentError(action, msg % args) | ||||||||||
|
||||||||||
# ======================= | ||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2192,12 +2192,24 @@ def test_wrong_argument_subparsers_no_destination_error(self): | |
subparsers.add_parser('foo') | ||
subparsers.add_parser('bar') | ||
with self.assertRaises(ArgumentParserError) as excinfo: | ||
parser.parse_args(('baz',)) | ||
parser.parse_args(('test',)) | ||
self.assertRegex( | ||
excinfo.exception.stderr, | ||
r"error: argument {foo,bar}: invalid choice: 'baz' \(choose from 'foo', 'bar'\)\n$" | ||
) | ||
|
||
def test_wrong_argument_subparsers_no_destination_error_with_closest_choice_input(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', maybe you meant bar? \(choose from 'foo', 'bar'\)\n$", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Note: |
||
) | ||
|
||
def test_optional_subparsers(self): | ||
parser = ErrorRaisingArgumentParser() | ||
subparsers = parser.add_subparsers(dest='command', required=False) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Add closest choice if exists in Argparser if wrong choice picked. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we'd probably want to move the import down into the case where the error is thrown, since this is probably not going to be used very often.