Skip to content

argparse sometimes tracebacks when all options in a mutually exclusive group are suppressed #96310

@dmach

Description

@dmach

Bug report

When a subparser contains a mutually exclusive group and the all options in the group are suppressed,
it sometimes errors out with a traceback. The crash depends on other options (their ordering and length) and terminal size.

Reproducer:

#!/usr/bin/python3

import argparse

parser = argparse.ArgumentParser()
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")

parser.parse_args()
$ python3.11 reproducer.py foo --help
Traceback (most recent call last):
  File ".../reproducer.py", line 13, in <module>
    parser.parse_args()
  File "/usr/lib64/python3.11/argparse.py", line 1862, in parse_args
    args, argv = self.parse_known_args(args, namespace)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.11/argparse.py", line 1895, in parse_known_args
    namespace, args = self._parse_known_args(args, namespace)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.11/argparse.py", line 2085, in _parse_known_args
    positionals_end_index = consume_positionals(start_index)
                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.11/argparse.py", line 2062, in consume_positionals
    take_action(action, args)
  File "/usr/lib64/python3.11/argparse.py", line 1971, in take_action
    action(self, namespace, argument_values, option_string)
  File "/usr/lib64/python3.11/argparse.py", line 1234, in __call__
    subnamespace, arg_strings = parser.parse_known_args(arg_strings, None)
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.11/argparse.py", line 1895, in parse_known_args
    namespace, args = self._parse_known_args(args, namespace)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.11/argparse.py", line 2103, in _parse_known_args
    start_index = consume_optional(start_index)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.11/argparse.py", line 2043, in consume_optional
    take_action(action, args, option_string)
  File "/usr/lib64/python3.11/argparse.py", line 1971, in take_action
    action(self, namespace, argument_values, option_string)
  File "/usr/lib64/python3.11/argparse.py", line 1112, in __call__
    parser.print_help()
  File "/usr/lib64/python3.11/argparse.py", line 2590, in print_help
    self._print_message(self.format_help(), file)
                        ^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.11/argparse.py", line 2574, in format_help
    return formatter.format_help()
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.11/argparse.py", line 286, in format_help
    help = self._root_section.format_help()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.11/argparse.py", line 217, in format_help
    item_help = join([func(*args) for func, args in self.items])
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.11/argparse.py", line 217, in <listcomp>
    item_help = join([func(*args) for func, args in self.items])
                      ^^^^^^^^^^^
  File "/usr/lib64/python3.11/argparse.py", line 341, in _format_usage
    assert ' '.join(opt_parts) == opt_usage
AssertionError

Please note that setting COLUMNS to a high number doesn't trigger the traceback:

COLUMNS=1000 python3.11 reproducer.py foo --help
usage: reproducer.py foo [-h]  [--longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong LONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONG]

options:
  -h, --help            show this help message and exit
  --longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong LONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONG

Your environment

  • CPython versions tested on: 3.8.13, 3.10.6, 3.11.0b5
  • openSUSE Tumbleweed 20220823

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    stdlibPython modules in the Lib dirtype-bugAn unexpected behavior, bug, or error

    Projects

    Status

    Doc issues

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      pFad - Phonifier reborn

      Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

      Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


      Alternative Proxies:

      Alternative Proxy

      pFad Proxy

      pFad v3 Proxy

      pFad v4 Proxy