From 29ccff39ddef8f969ef61fe3f8a300350123c4d3 Mon Sep 17 00:00:00 2001 From: Lazizbek Ergashev Date: Tue, 8 Sep 2026 01:44:40 +0500 Subject: [PATCH 1/2] gh-157127: Separate names with commas in argparse mutually exclusive group error --- Doc/library/argparse.rst | 2 +- Lib/argparse.py | 2 +- Lib/test/test_argparse.py | 6 +++--- .../Library/2026-09-08-00-31-56.gh-issue-157127.gduPd0.rst | 2 ++ 4 files changed, 7 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-09-08-00-31-56.gh-issue-157127.gduPd0.rst diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst index fc5302d875fc1f..1a2f072b0bf11e 100644 --- a/Doc/library/argparse.rst +++ b/Doc/library/argparse.rst @@ -2099,7 +2099,7 @@ Mutual exclusion >>> group.add_argument('--bar', action='store_false') >>> parser.parse_args([]) usage: PROG [-h] (--foo | --bar) - PROG: error: one of the arguments --foo --bar is required + PROG: error: one of the arguments --foo, --bar is required Note that currently mutually exclusive argument groups do not support the *title* and *description* arguments of diff --git a/Lib/argparse.py b/Lib/argparse.py index 38e1c0d0ed78fd..797f05627674cf 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -2500,7 +2500,7 @@ def consume_positionals(start_index): for action in group._group_actions if action.help is not SUPPRESS] msg = _('one of the arguments %s is required') - raise ArgumentError(None, msg % ' '.join(names)) + raise ArgumentError(None, msg % ', '.join(names)) # return the updated namespace and the extra arguments return namespace, extras diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 75beb5ede13fef..168b4310923c85 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -6866,7 +6866,7 @@ def test_required_exclusive(self): args = parser.parse_intermixed_args('1 --foo 2'.split()) self.assertEqual(NS(badger=['1', '2'], foo=True, spam=None), args) self.assertRaisesRegex(argparse.ArgumentError, - 'one of the arguments --foo --spam is required', + 'one of the arguments --foo, --spam is required', parser.parse_intermixed_args, '1 2'.split()) self.assertEqual(group.required, True) @@ -6882,7 +6882,7 @@ def test_required_exclusive_with_positional(self): args = parser.parse_intermixed_args(['a', 'b']) self.assertEqual(NS(foo=False, spam=None, badger=['a', 'b']), args) self.assertRaisesRegex(argparse.ArgumentError, - 'one of the arguments --foo --spam badger is required', + 'one of the arguments --foo, --spam, badger is required', parser.parse_intermixed_args, []) self.assertRaisesRegex(argparse.ArgumentError, 'argument badger: not allowed with argument --foo', @@ -7258,7 +7258,7 @@ def test_required_mutually_exclusive_args(self): group.add_argument('--bar') group.add_argument('--baz') self.assertRaisesRegex(argparse.ArgumentError, - 'one of the arguments --bar --baz is required', + 'one of the arguments --bar, --baz is required', self.parser.parse_args, []) def test_conflicting_mutually_exclusive_args_optional_with_metavar(self): diff --git a/Misc/NEWS.d/next/Library/2026-09-08-00-31-56.gh-issue-157127.gduPd0.rst b/Misc/NEWS.d/next/Library/2026-09-08-00-31-56.gh-issue-157127.gduPd0.rst new file mode 100644 index 00000000000000..748a3d9a04ba48 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-09-08-00-31-56.gh-issue-157127.gduPd0.rst @@ -0,0 +1,2 @@ +Separate the argument names with commas in the :mod:`argparse` error message +reported for a missing required mutually exclusive group. From 3f7606e56de9256d57b41a88a52376e983dd9c5b Mon Sep 17 00:00:00 2001 From: Lazizbek Ergashev Date: Wed, 9 Sep 2026 13:36:05 +0500 Subject: [PATCH 2/2] gh-157127: Move the argument names to the end of the error message --- Doc/library/argparse.rst | 2 +- Lib/argparse.py | 2 +- Lib/test/test_argparse.py | 6 +++--- Lib/test/translationdata/argparse/msgids.txt | 2 +- .../Library/2026-09-08-00-31-56.gh-issue-157127.gduPd0.rst | 5 +++-- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst index 1a2f072b0bf11e..c88c10030fb369 100644 --- a/Doc/library/argparse.rst +++ b/Doc/library/argparse.rst @@ -2099,7 +2099,7 @@ Mutual exclusion >>> group.add_argument('--bar', action='store_false') >>> parser.parse_args([]) usage: PROG [-h] (--foo | --bar) - PROG: error: one of the arguments --foo, --bar is required + PROG: error: one of the following arguments is required: --foo, --bar Note that currently mutually exclusive argument groups do not support the *title* and *description* arguments of diff --git a/Lib/argparse.py b/Lib/argparse.py index 797f05627674cf..4cf5dca148c969 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -2499,7 +2499,7 @@ def consume_positionals(start_index): names = [_get_action_name(action) for action in group._group_actions if action.help is not SUPPRESS] - msg = _('one of the arguments %s is required') + msg = _('one of the following arguments is required: %s') raise ArgumentError(None, msg % ', '.join(names)) # return the updated namespace and the extra arguments diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 168b4310923c85..3abdb3244570a5 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -6866,7 +6866,7 @@ def test_required_exclusive(self): args = parser.parse_intermixed_args('1 --foo 2'.split()) self.assertEqual(NS(badger=['1', '2'], foo=True, spam=None), args) self.assertRaisesRegex(argparse.ArgumentError, - 'one of the arguments --foo, --spam is required', + 'one of the following arguments is required: --foo, --spam', parser.parse_intermixed_args, '1 2'.split()) self.assertEqual(group.required, True) @@ -6882,7 +6882,7 @@ def test_required_exclusive_with_positional(self): args = parser.parse_intermixed_args(['a', 'b']) self.assertEqual(NS(foo=False, spam=None, badger=['a', 'b']), args) self.assertRaisesRegex(argparse.ArgumentError, - 'one of the arguments --foo, --spam, badger is required', + 'one of the following arguments is required: --foo, --spam, badger', parser.parse_intermixed_args, []) self.assertRaisesRegex(argparse.ArgumentError, 'argument badger: not allowed with argument --foo', @@ -7258,7 +7258,7 @@ def test_required_mutually_exclusive_args(self): group.add_argument('--bar') group.add_argument('--baz') self.assertRaisesRegex(argparse.ArgumentError, - 'one of the arguments --bar, --baz is required', + 'one of the following arguments is required: --bar, --baz', self.parser.parse_args, []) def test_conflicting_mutually_exclusive_args_optional_with_metavar(self): diff --git a/Lib/test/translationdata/argparse/msgids.txt b/Lib/test/translationdata/argparse/msgids.txt index ae89ac74726ecf..8a2305b89a5667 100644 --- a/Lib/test/translationdata/argparse/msgids.txt +++ b/Lib/test/translationdata/argparse/msgids.txt @@ -18,7 +18,7 @@ invalid %(type)s value: %(value)r invalid choice: %(value)r (choose from %(choices)s) invalid choice: %(value)r, maybe you meant %(closest)r? (choose from %(choices)s) not allowed with argument %s -one of the arguments %s is required +one of the following arguments is required: %s option '%(option)s' is deprecated options positional arguments diff --git a/Misc/NEWS.d/next/Library/2026-09-08-00-31-56.gh-issue-157127.gduPd0.rst b/Misc/NEWS.d/next/Library/2026-09-08-00-31-56.gh-issue-157127.gduPd0.rst index 748a3d9a04ba48..614c85fafa264a 100644 --- a/Misc/NEWS.d/next/Library/2026-09-08-00-31-56.gh-issue-157127.gduPd0.rst +++ b/Misc/NEWS.d/next/Library/2026-09-08-00-31-56.gh-issue-157127.gduPd0.rst @@ -1,2 +1,3 @@ -Separate the argument names with commas in the :mod:`argparse` error message -reported for a missing required mutually exclusive group. +Reword the :mod:`argparse` error message reported for a missing required +mutually exclusive group to ``one of the following arguments is required: +--foo, --bar``, so that the argument names are separated by commas.