From 4c79b5af8c1ef14904041fd9986354c0d851e734 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Wed, 18 Feb 2026 20:30:07 -0800 Subject: [PATCH 1/5] Ensure quotes around suggestions --- Lib/argparse.py | 8 +++++++- Lib/test/test_argparse.py | 14 +++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/Lib/argparse.py b/Lib/argparse.py index 296a210ad832da..1b2f595c22d08e 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -2757,8 +2757,14 @@ def _check_value(self, action, value): choices = iter(choices) if value not in choices: + def _format_choice(choice): + # For enum members, use repr of the value, not the enum itself + if hasattr(choice, 'value'): + return repr(choice.value) + return repr(choice) + args = {'value': str(value), - 'choices': ', '.join(map(str, action.choices))} + 'choices': ', '.join(map(_format_choice, action.choices))} msg = _('invalid choice: %(value)r (choose from %(choices)s)') if self.suggest_on_error and isinstance(value, str): diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 4526efe4b80ef4..ace477f2fe2013 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -1123,7 +1123,7 @@ def test_invalid_enum_value_raises_error(self): parser.add_argument('--color', choices=self.Color) self.assertRaisesRegex( argparse.ArgumentError, - r"invalid choice: 'yellow' \(choose from red, green, blue\)", + r"invalid choice: 'yellow' \(choose from 'red', 'green', 'blue'\)", parser.parse_args, ['--color', 'yellow'], ) @@ -2392,7 +2392,7 @@ def test_wrong_argument_error_with_suggestions(self): 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)", + "error: argument foo: invalid choice: 'bazz', maybe you meant 'baz'? (choose from 'bar', 'baz')", excinfo.exception.stderr ) @@ -2402,7 +2402,7 @@ def test_wrong_argument_error_no_suggestions(self): with self.assertRaises(ArgumentParserError) as excinfo: parser.parse_args(('bazz',)) self.assertIn( - "error: argument foo: invalid choice: 'bazz' (choose from bar, baz)", + "error: argument foo: invalid choice: 'bazz' (choose from 'bar', 'baz')", excinfo.exception.stderr, ) @@ -2415,7 +2415,7 @@ def test_wrong_argument_subparsers_with_suggestions(self): parser.parse_args(('baz',)) self.assertIn( "error: argument {foo,bar}: invalid choice: 'baz', maybe you meant" - " 'bar'? (choose from foo, bar)", + " 'bar'? (choose from 'foo', 'bar')", excinfo.exception.stderr, ) @@ -2427,7 +2427,7 @@ def test_wrong_argument_subparsers_no_suggestions(self): with self.assertRaises(ArgumentParserError) as excinfo: parser.parse_args(('baz',)) self.assertIn( - "error: argument {foo,bar}: invalid choice: 'baz' (choose from foo, bar)", + "error: argument {foo,bar}: invalid choice: 'baz' (choose from 'foo', 'bar')", excinfo.exception.stderr, ) @@ -2438,7 +2438,7 @@ def test_wrong_argument_with_suggestion_explicit(self): parser.parse_args(('bazz',)) self.assertIn( "error: argument foo: invalid choice: 'bazz', maybe you meant" - " 'baz'? (choose from bar, baz)", + " 'baz'? (choose from 'bar', 'baz')", excinfo.exception.stderr, ) @@ -2468,7 +2468,7 @@ def test_suggestions_choices_mixed_types(self): with self.assertRaises(ArgumentParserError) as excinfo: parser.parse_args(('3',)) self.assertIn( - "error: argument foo: invalid choice: '3' (choose from 1, 2)", + "error: argument foo: invalid choice: '3' (choose from 1, '2')", excinfo.exception.stderr, ) From f29635dc747ae2859c0b0b2ab492351690002840 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Thu, 19 Feb 2026 04:41:03 +0000 Subject: [PATCH 2/5] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2026-02-19-04-40-57.gh-issue-130750.0hW52O.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2026-02-19-04-40-57.gh-issue-130750.0hW52O.rst diff --git a/Misc/NEWS.d/next/Library/2026-02-19-04-40-57.gh-issue-130750.0hW52O.rst b/Misc/NEWS.d/next/Library/2026-02-19-04-40-57.gh-issue-130750.0hW52O.rst new file mode 100644 index 00000000000000..1cdedba9709e30 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-02-19-04-40-57.gh-issue-130750.0hW52O.rst @@ -0,0 +1 @@ +Restore quoting of choices in :mod:`argparse` error messages for improved clarity and consistency with documentation. Enum members are handled specially to show just their quoted values. From 75defab960515b0b7db449abbb015a8f5bcfabaa Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 4 May 2026 11:28:50 -0700 Subject: [PATCH 3/5] Just quote everything, since its all a str anyway --- Lib/argparse.py | 8 +------- Lib/test/test_argparse.py | 2 +- .../2026-02-19-04-40-57.gh-issue-130750.0hW52O.rst | 3 ++- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Lib/argparse.py b/Lib/argparse.py index 54c35e7e9feed2..9ea6b32163a9d1 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -2757,14 +2757,8 @@ def _check_value(self, action, value): choices = iter(choices) if value not in choices: - def _format_choice(choice): - # For enum members, use repr of the value, not the enum itself - if hasattr(choice, 'value'): - return repr(choice.value) - return repr(choice) - args = {'value': str(value), - 'choices': ', '.join(map(_format_choice, action.choices))} + 'choices': ', '.join(repr(str(choice)) for choice in action.choices)} msg = _('invalid choice: %(value)r (choose from %(choices)s)') if self.suggest_on_error and isinstance(value, str): diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 84e83fb3d9512d..2a40ebbd076733 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -2468,7 +2468,7 @@ def test_suggestions_choices_mixed_types(self): with self.assertRaises(ArgumentParserError) as excinfo: parser.parse_args(('3',)) self.assertIn( - "error: argument foo: invalid choice: '3' (choose from 1, '2')", + "error: argument foo: invalid choice: '3' (choose from '1', '2')", excinfo.exception.stderr, ) diff --git a/Misc/NEWS.d/next/Library/2026-02-19-04-40-57.gh-issue-130750.0hW52O.rst b/Misc/NEWS.d/next/Library/2026-02-19-04-40-57.gh-issue-130750.0hW52O.rst index 1cdedba9709e30..8bca48ab159476 100644 --- a/Misc/NEWS.d/next/Library/2026-02-19-04-40-57.gh-issue-130750.0hW52O.rst +++ b/Misc/NEWS.d/next/Library/2026-02-19-04-40-57.gh-issue-130750.0hW52O.rst @@ -1 +1,2 @@ -Restore quoting of choices in :mod:`argparse` error messages for improved clarity and consistency with documentation. Enum members are handled specially to show just their quoted values. +Restore quoting of choices in :mod:`argparse` error messages for improved clarity and consistency with documentation. + From 06251645b50453516adedeb2dbcec7965ba59a89 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 4 May 2026 14:16:31 -0700 Subject: [PATCH 4/5] Missed tests --- Lib/test/test_argparse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 2a40ebbd076733..4799c9947dd3ee 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -2458,7 +2458,7 @@ def test_suggestions_choices_int(self): with self.assertRaises(ArgumentParserError) as excinfo: parser.parse_args(('3',)) self.assertIn( - "error: argument foo: invalid choice: '3' (choose from 1, 2)", + "error: argument foo: invalid choice: '3' (choose from '1', '2')", excinfo.exception.stderr, ) From e33550c5293a3bb864f29485e1f61313dac07ced Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 4 May 2026 14:24:19 -0700 Subject: [PATCH 5/5] one more test --- Lib/test/test_timeit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_timeit.py b/Lib/test/test_timeit.py index 81f1a9c97393d1..a2a09f9de61490 100644 --- a/Lib/test/test_timeit.py +++ b/Lib/test/test_timeit.py @@ -359,7 +359,7 @@ def test_main_with_time_unit(self): seconds_per_increment=0.003, switches=["-u", "parsec"] ) self.assertIn( - "choose from nsec, usec, msec, sec", error_stringio.getvalue() + "choose from 'nsec', 'usec', 'msec', 'sec'", error_stringio.getvalue() ) def test_main_exception(self):