Skip to content

argparse mutually exclusive group does not exclude in some cases #85531

Closed as not planned

Description

@kkarbowiak
BPO 41359
Nosy @rhettinger

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields:

assignee = None
closed_at = None
created_at = <Date 2020-07-21.11:46:47.665>
labels = ['type-bug', 'library', '3.10']
title = 'argparse mutually exclusive group  does not exclude in some cases'
updated_at = <Date 2020-07-21.17:08:13.980>
user = 'https://bugs.python.org/kkarbowiak'

bugs.python.org fields:

activity = <Date 2020-07-21.17:08:13.980>
actor = 'paul.j3'
assignee = 'none'
closed = False
closed_date = None
closer = None
components = ['Library (Lib)']
creation = <Date 2020-07-21.11:46:47.665>
creator = 'kkarbowiak'
dependencies = []
files = []
hgrepos = []
issue_num = 41359
keywords = []
message_count = 2.0
messages = ['374065', '374073']
nosy_count = 3.0
nosy_names = ['rhettinger', 'paul.j3', 'kkarbowiak']
pr_nums = []
priority = 'normal'
resolution = None
stage = None
status = 'open'
superseder = None
type = 'behavior'
url = 'https://bugs.python.org/issue41359'
versions = ['Python 3.10']

Activity

  1. kkarbowiak commented on Jul 21, 2020

    kkarbowiakmannequin
    MannequinAuthor

    The documentation for ArgumentParser.add_mutually_exclusive_group states: "argparse will make sure that only one of the arguments in the mutually exclusive group was present on the command line".

    This is not the case in certain circumstances:

    import argparse
    
    parser = argparse.ArgumentParser()
    group = parser.add_mutually_exclusive_group()
    group.add_argument('-a')
    group.add_argument('-b', nargs='?')
    
    parser.parse_args('-a a -b'.split())

    The above code does not produce any error, even though both exclusive arguments are present.

    My guess is that the check for mutual exclusion is not done during processing of each command line argument, but rather afterwards. It seems the check only ensures at most one argument from group is not None.

    The issue exists at least on Python 2.7.13, 3.6, 3.7.5, 3.8, and 3.10.

  2. added
    stdlibStandard Library Python modules in the Lib/ directory
    type-bugAn unexpected behavior, bug, or error
    on Jul 21, 2020
  3. paulj3 commented on Jul 21, 2020

    paulj3mannequin
    Mannequin

    This is the result of how default values are handled with '?' (and '*') nargs.

    At the start of nested take_action function (which handles all actions) is this code:

                argument_values = self._get_values(action, argument_strings)
    
                # error if this argument is not allowed with other previously
                # seen arguments, assuming that actions that use the default
                # value don't really count as "present"
                if argument_values is not action.default:
                    seen_non_default_actions.add(action)
                    for conflict_action in action_conflicts.get(action, []):
                        if conflict_action in seen_non_default_actions:
                            msg = _('not allowed with argument %s')
                            action_name = _get_action_name(conflict_action)
                            raise ArgumentError(action, msg % action_name)

    'get_values' gets the values for this action. A bare '-b' will be given its 'default'.

    An optional positional is always 'seen', since an empty list satisfies its 'nargs'. 'get_values' assigns the default instead. This code in take_action allows us to include such positionals in a mutually exclusive group.

    This handing of the '?' optional is a byproduct of that code. Normally we provide both a 'default' and a 'const' with such an argument, giving us a 3-way switch (default, const or user-value). If either 'default' or 'const' is provided, your '-b' will behave as expected.

    I have a feeling that trying to document this edge case will just make things more confusing.

  4. transferred this issue fromon Apr 10, 2022
  5. serhiy-storchaka commented on Sep 21, 2024

    @serhiy-storchaka
    Member

    This looks similar to #63143, and #124307 fixes also this issue.

  6. moved this from Bugs to Doc issues in Argparse issueson Sep 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.10 (EOL)end of lifestdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions