From 616c5fee9630886daf79d649a0db8f55c4e8384d Mon Sep 17 00:00:00 2001 From: Daniel Mach Date: Fri, 26 Aug 2022 15:05:08 +0200 Subject: [PATCH 1/2] gh-96310: Fix a traceback in argparse when all options in a mutually exclusive group are suppressed Reproducer depends on terminal size - the traceback occurs when there's an option long enough so the usage line doesn't fit the terminal width. Option order is also important for reproducibility. Excluding empty groups (with all options suppressed) from inserts fixes the problem. --- Lib/argparse.py | 2 ++ Lib/test/test_argparse.py | 19 +++++++++++++++++++ ...2-08-26-15-50-53.gh-issue-96310.0NssDh.rst | 2 ++ 3 files changed, 23 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2022-08-26-15-50-53.gh-issue-96310.0NssDh.rst diff --git a/Lib/argparse.py b/Lib/argparse.py index fe48f8670fa20a..808e1401dba494 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -405,6 +405,8 @@ def _format_actions_usage(self, actions, groups): else: end = start + len(group._group_actions) if actions[start:end] == group._group_actions: + if all((action.help is SUPPRESS for action in group._group_actions)): + continue for action in group._group_actions: group_actions.add(action) if not group.required: diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 2b7f008d38564b..f2a625a972b4e1 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -2695,6 +2695,25 @@ def test_help(self): ''' self.assertEqual(parser.format_help(), textwrap.dedent(expected)) + def test_help_subparser_all_mutually_exclusive_group_members_suppressed(self): + self.maxDiff = None + parser = ErrorRaisingArgumentParser(prog='PROG') + commands = parser.add_subparsers(title="commands", dest="command") + cmd_foo = commands.add_parser("foo") + group = cmd_foo.add_mutually_exclusive_group() + group.add_argument('--verbose', action='store_true', help=argparse.SUPPRESS) + group.add_argument('--quiet', action='store_true', help=argparse.SUPPRESS) + cmd_foo.add_argument("--longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong") + expected = '''\ + usage: PROG foo [-h] + [--longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong LONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONG] + + options: + -h, --help show this help message and exit + --longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong LONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONG + ''' + self.assertEqual(cmd_foo.format_help(), textwrap.dedent(expected)) + def test_empty_group(self): # See issue 26952 parser = argparse.ArgumentParser() diff --git a/Misc/NEWS.d/next/Library/2022-08-26-15-50-53.gh-issue-96310.0NssDh.rst b/Misc/NEWS.d/next/Library/2022-08-26-15-50-53.gh-issue-96310.0NssDh.rst new file mode 100644 index 00000000000000..f302b5a3f89150 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-08-26-15-50-53.gh-issue-96310.0NssDh.rst @@ -0,0 +1,2 @@ +Fix a traceback in argparse when all options in a mutually exclusive group +are suppressed. From 3b9b636cb5502cb716519c85b4f72f8bc7dde8fe Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 21 Feb 2024 15:24:40 +0200 Subject: [PATCH 2/2] Polishing. --- Lib/argparse.py | 4 ++-- Lib/test/test_argparse.py | 10 ++++++---- .../2022-08-26-15-50-53.gh-issue-96310.0NssDh.rst | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Lib/argparse.py b/Lib/argparse.py index eb970cb848b80f..f86658baf7f2ba 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -405,8 +405,6 @@ def _format_actions_usage(self, actions, groups): group_action_count = len(group._group_actions) end = start + group_action_count if actions[start:end] == group._group_actions: - if all((action.help is SUPPRESS for action in group._group_actions)): - continue suppressed_actions_count = 0 for action in group._group_actions: @@ -415,6 +413,8 @@ def _format_actions_usage(self, actions, groups): suppressed_actions_count += 1 exposed_actions_count = group_action_count - suppressed_actions_count + if not exposed_actions_count: + continue if not group.required: if start in inserts: diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 176e5dec59c021..617b1721f3dbb1 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -2872,14 +2872,16 @@ def test_help_subparser_all_mutually_exclusive_group_members_suppressed(self): group = cmd_foo.add_mutually_exclusive_group() group.add_argument('--verbose', action='store_true', help=argparse.SUPPRESS) group.add_argument('--quiet', action='store_true', help=argparse.SUPPRESS) - cmd_foo.add_argument("--longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong") - expected = '''\ + longopt = '--' + 'long'*32 + longmeta = 'LONG'*32 + cmd_foo.add_argument(longopt) + expected = f'''\ usage: PROG foo [-h] - [--longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong LONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONG] + [{longopt} {longmeta}] options: -h, --help show this help message and exit - --longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong LONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONG + {longopt} {longmeta} ''' self.assertEqual(cmd_foo.format_help(), textwrap.dedent(expected)) diff --git a/Misc/NEWS.d/next/Library/2022-08-26-15-50-53.gh-issue-96310.0NssDh.rst b/Misc/NEWS.d/next/Library/2022-08-26-15-50-53.gh-issue-96310.0NssDh.rst index f302b5a3f89150..f8efb0002e104a 100644 --- a/Misc/NEWS.d/next/Library/2022-08-26-15-50-53.gh-issue-96310.0NssDh.rst +++ b/Misc/NEWS.d/next/Library/2022-08-26-15-50-53.gh-issue-96310.0NssDh.rst @@ -1,2 +1,2 @@ -Fix a traceback in argparse when all options in a mutually exclusive group -are suppressed. +Fix a traceback in :mod:`argparse` when all options in a mutually exclusive +group are suppressed.