Skip to content
8 changes: 6 additions & 2 deletions Lib/argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,13 +398,17 @@ def _format_actions_usage(self, actions, groups):
raise ValueError(f'empty group {group}')

try:
start = actions.index(group._group_actions[0])
start = _sys.maxsize
for item in group._group_actions:
index = actions.index(item)
if index < start:
start = index
except ValueError:
continue
else:
group_action_count = len(group._group_actions)
end = start + group_action_count
if actions[start:end] == group._group_actions:
if set(actions[start:end]) == set(group._group_actions):

suppressed_actions_count = 0
for action in group._group_actions:
Expand Down
23 changes: 23 additions & 0 deletions Lib/test/test_argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -2836,6 +2836,29 @@ def test_help(self):
'''
self.assertEqual(parser.format_help(), textwrap.dedent(expected))

def test_optional_order(self):
parser = ErrorRaisingArgumentParser(prog="PROG")
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("-", dest="FOO")
group.add_argument("bar", nargs="?")
expected = '''\
usage: PROG [-h] (- FOO | bar)

positional arguments:
bar

options:
-h, --help show this help message and exit
- FOO
'''
self.assertEqual(parser.format_help(), textwrap.dedent(expected))

parser = ErrorRaisingArgumentParser(prog="PROG")
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("bar", nargs="?")
group.add_argument("-", dest="FOO")
self.assertEqual(parser.format_help(), textwrap.dedent(expected))

def test_empty_group(self):
# See issue 26952
parser = argparse.ArgumentParser()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Correct argparse usage output for required, mutually exclusive groups containing a positional argument