]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-121151: argparse: Fix wrapping of long usage text of arguments inside a mutually...
authorAli Hamdan <ali.hamdan.dev@gmail.com>
Wed, 7 Aug 2024 13:20:38 +0000 (15:20 +0200)
committerGitHub <noreply@github.com>
Wed, 7 Aug 2024 13:20:38 +0000 (15:20 +0200)
Lib/argparse.py
Lib/test/test_argparse.py
Misc/NEWS.d/next/Library/2024-06-29-15-23-26.gh-issue-121151.HeLEvq.rst [new file with mode: 0644]

index 83189115bf85f7cefb96390b5f5b3e729d391d53..100ef9f55cd2f78a2b5f09c155c3e009aafb5b6d 100644 (file)
@@ -447,15 +447,24 @@ class HelpFormatter(object):
             parts.append(part)
 
         # group mutually exclusive actions
+        inserted_separators_indices = set()
         for start, end in sorted(inserts, reverse=True):
             group = inserts[start, end]
             group_parts = [item for item in parts[start:end] if item is not None]
+            group_size = len(group_parts)
             if group.required:
-                open, close = "()" if len(group_parts) > 1 else ("", "")
+                open, close = "()" if group_size > 1 else ("", "")
             else:
                 open, close = "[]"
-            parts[start] = open + " | ".join(group_parts) + close
-            for i in range(start + 1, end):
+            group_parts[0] = open + group_parts[0]
+            group_parts[-1] = group_parts[-1] + close
+            for i, part in enumerate(group_parts[:-1], start=start):
+                # insert a separator if not already done in a nested group
+                if i not in inserted_separators_indices:
+                    parts[i] = part + ' |'
+                    inserted_separators_indices.add(i)
+            parts[start + group_size - 1] = group_parts[-1]
+            for i in range(start + group_size, end):
                 parts[i] = None
 
         # return the usage parts
index 84fe74a1063fb6351df014a7fa10b662d7aaf5f5..fd111be18aed6eb5178f63656b8cd5efd4404583 100644 (file)
@@ -2959,12 +2959,12 @@ class TestMutuallyExclusiveLong(MEMixin, TestCase):
     ]
 
     usage_when_not_required = '''\
-    usage: PROG [-h] [--abcde ABCDE] [--fghij FGHIJ]
-                [--klmno KLMNO | --pqrst PQRST]
+    usage: PROG [-h] [--abcde ABCDE] [--fghij FGHIJ] [--klmno KLMNO |
+                --pqrst PQRST]
     '''
     usage_when_required = '''\
-    usage: PROG [-h] [--abcde ABCDE] [--fghij FGHIJ]
-                (--klmno KLMNO | --pqrst PQRST)
+    usage: PROG [-h] [--abcde ABCDE] [--fghij FGHIJ] (--klmno KLMNO |
+                --pqrst PQRST)
     '''
     help = '''\
 
@@ -4347,6 +4347,24 @@ class TestHelpUsageNoWhitespaceCrash(TestCase):
         ''')
         self.assertEqual(parser.format_usage(), usage)
 
+    def test_long_mutex_groups_wrap(self):
+        parser = argparse.ArgumentParser(prog='PROG')
+        g = parser.add_mutually_exclusive_group()
+        g.add_argument('--op1', metavar='MET', nargs='?')
+        g.add_argument('--op2', metavar=('MET1', 'MET2'), nargs='*')
+        g.add_argument('--op3', nargs='*')
+        g.add_argument('--op4', metavar=('MET1', 'MET2'), nargs='+')
+        g.add_argument('--op5', nargs='+')
+        g.add_argument('--op6', nargs=3)
+        g.add_argument('--op7', metavar=('MET1', 'MET2', 'MET3'), nargs=3)
+
+        usage = textwrap.dedent('''\
+        usage: PROG [-h] [--op1 [MET] | --op2 [MET1 [MET2 ...]] | --op3 [OP3 ...] |
+                    --op4 MET1 [MET2 ...] | --op5 OP5 [OP5 ...] | --op6 OP6 OP6 OP6 |
+                    --op7 MET1 MET2 MET3]
+        ''')
+        self.assertEqual(parser.format_usage(), usage)
+
 
 class TestHelpVariableExpansion(HelpTestCase):
     """Test that variables are expanded properly in help messages"""
diff --git a/Misc/NEWS.d/next/Library/2024-06-29-15-23-26.gh-issue-121151.HeLEvq.rst b/Misc/NEWS.d/next/Library/2024-06-29-15-23-26.gh-issue-121151.HeLEvq.rst
new file mode 100644 (file)
index 0000000..f08b613
--- /dev/null
@@ -0,0 +1,2 @@
+Fix wrapping of long usage text of arguments inside a mutually exclusive
+group in :mod:`argparse`.