diff --git a/Lib/argparse.py b/Lib/argparse.py index 690b2a9db9481b1..b7c2b0451f056bf 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -548,8 +548,7 @@ def _metavar_formatter(self, action, default_metavar): if action.metavar is not None: result = action.metavar elif action.choices is not None: - choice_strs = [str(choice) for choice in action.choices] - result = '{%s}' % ','.join(choice_strs) + result = '{%s}' % ','.join(map(str, action.choices)) else: result = default_metavar @@ -597,8 +596,7 @@ def _expand_help(self, action): if hasattr(params[name], '__name__'): params[name] = params[name].__name__ if params.get('choices') is not None: - choices_str = ', '.join([str(c) for c in params['choices']]) - params['choices'] = choices_str + params['choices'] = ', '.join(map(str, params['choices'])) return self._get_help_string(action) % params def _iter_indented_subactions(self, action): @@ -707,7 +705,7 @@ def _get_action_name(argument): elif argument.dest not in (None, SUPPRESS): return argument.dest elif argument.choices: - return '{' + ','.join(argument.choices) + '}' + return '{%s}' % ','.join(map(str, argument.choices)) else: return None @@ -2556,8 +2554,8 @@ 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)') + 'choices': ', '.join(map(str, action.choices))} + msg = _('invalid choice: %(value)s (choose from %(choices)s)') raise ArgumentError(action, msg % args) # ======================= diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index ef05a6fefcffcce..ab55e8a55696739 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -15,6 +15,7 @@ import argparse import warnings +from enum import StrEnum from test.support import os_helper, captured_stderr from unittest import mock @@ -952,6 +953,35 @@ class TestDisallowLongAbbreviationAllowsShortGroupingPrefix(ParserTestCase): ] +class TestStrEnumChoices(TestCase): + class Color(StrEnum): + RED = "red" + GREEN = "green" + BLUE = "blue" + + def test_metavar_formatter_with_strenum(self): + parser = argparse.ArgumentParser() + parser.add_argument('--color', choices=self.Color) + args = parser.parse_args(['--color', 'red']) + self.assertEqual(args.color, self.Color.RED) + + def test_expand_help_with_strenum(self): + parser = argparse.ArgumentParser() + parser.add_argument('--color', choices=self.Color, help='Choose a color') + help_output = parser.format_help() + self.assertIn('[--color {red,green,blue}]', help_output) + self.assertIn(' --color {red,green,blue}', help_output) + + def test_check_value_with_strenum(self): + parser = argparse.ArgumentParser() + parser.add_argument('--color', choices=self.Color) + self.assertRaisesRegex( + argparse.ArgumentError, + r"invalid choice: yellow \(choose from red, green, blue\)", + parser.parse_args, + ['--color', 'yellow'], + ) + # ================ # Positional tests # ================ @@ -2399,7 +2429,7 @@ def test_wrong_argument_subparsers_no_destination_error(self): parser.parse_args(('baz',)) self.assertRegex( excinfo.exception.stderr, - r"error: argument {foo,bar}: invalid choice: 'baz' \(choose from 'foo', 'bar'\)\n$" + r"error: argument {foo,bar}: invalid choice: baz \(choose from foo, bar\)\n$" ) def test_optional_subparsers(self): @@ -6061,7 +6091,7 @@ def test_subparser(self): args = parser.parse_args(['x', '--', 'run', '--', 'a', '--', 'b']) self.assertEqual(NS(foo='x', f=None, bar=['a', '--', 'b']), args) self.assertRaisesRegex(argparse.ArgumentError, - "invalid choice: '--'", + "invalid choice: --", parser.parse_args, ['--', 'x', '--', 'run', 'a', 'b']) def test_subparser_after_multiple_argument_option(self): @@ -6075,7 +6105,7 @@ def test_subparser_after_multiple_argument_option(self): args = parser.parse_args(['--foo', 'x', 'y', '--', 'run', 'a', 'b', '-f', 'c']) self.assertEqual(NS(foo=['x', 'y'], f='c', bar=['a', 'b']), args) self.assertRaisesRegex(argparse.ArgumentError, - "invalid choice: '--'", + "invalid choice: --", parser.parse_args, ['--foo', 'x', '--', '--', 'run', 'a', 'b']) diff --git a/Misc/NEWS.d/next/Library/2024-04-19-05-58-50.gh-issue-117766.J3xepp.rst b/Misc/NEWS.d/next/Library/2024-04-19-05-58-50.gh-issue-117766.J3xepp.rst new file mode 100644 index 000000000000000..d090f931f0238d9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-04-19-05-58-50.gh-issue-117766.J3xepp.rst @@ -0,0 +1 @@ +Always use :func:`str` to print ``choices`` in :mod:`argparse`.