Title: argparse improperly prints mutually exclusive options when they are in a group
Type: behavior Stage:
Components: Library (Lib) Versions: Python 3.5, Python 2.7
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: Sam.Kerr, paul.j3
Priority: normal Keywords: patch

Created on 2014-07-23 16:03 by Sam.Kerr, last changed 2017-02-01 02:34 by paul.j3.

File name Uploaded Description Edit
issue22047_1.patch paul.j3, 2014-07-25 21:37 review paul.j3, 2014-07-25 22:52
issue22047_2.patch paul.j3, 2014-07-26 06:05 review
Messages (9)
msg223744 - (view) Author: Sam Kerr (Sam.Kerr) Date: 2014-07-23 16:03
The following code:

   import argparse
   parser = argparse.ArgumentParser()
   group1 = parser.add_mutually_exclusive_group()
   group2 = group1.add_mutually_exclusive_group()
   group2.add_argument('-hello',action='store_true', help="A flag")
   args = parser.parse_args()

produces this output:

   skerr@gravel:~$ python -h
   usage: [-h] [[-hello]

   optional arguments:
        -h, --help  show this help message and exit
        -hello      A flag

Note the double [[ around hello, but there is no double ]] to close it. This is the error.
msg223917 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2014-07-25 03:50
That's an artifact of how the group usage is formatted (which isn't very robust).  It's not designed to handle nested groups.  

Mutually exclusive groups aren't meant to nest inside other mutually exclusive groups.  While it possible, it doesn't help you.  Effectively the arguments combined into one larger group.

See for discussion on how nested groups might be implemented, and the difficulties in properly formatting their usage. has a more robust mutually exclusive group formatter, but even that is not designed for nesting.
msg223972 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2014-07-25 17:24
On further thought, I think

   group2 = group1.add_mutually_exclusive_group()

should have raised an error, stating that a group (argument or mutually exclusive) cannot be added to a mutually exclusive group.

Also an argument group should not be added to another argument group.

However, adding a mutually exclusive group to an argument group is ok, though only for the undocumented (though tested) purpose of giving it a title.
msg223974 - (view) Author: Sam Kerr (Sam.Kerr) Date: 2014-07-25 17:28
What I was going for was the ability to have a group contain a mutually exclusive group and a non-exclusive group, but using the mutually exclusive group meant you could not use the non-exclusive group.

Such as:

[ [ -opt1 | -opt2 | -opt3   ] [ [-opt4] [-opt5] [-opt6] ] ]
msg223978 - (view) Author: Sam Kerr (Sam.Kerr) Date: 2014-07-25 17:37
I fat fingered the example, sorry:

[ [ -opt1 | -opt2 | -opt3   ] | [ [-opt4] [-opt5] [-opt6] ] ]

Note the new pipe between the 2 subgroups
msg224004 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2014-07-25 21:37
Here's a preliminary patch that raises an error if there's an attempt to nest a mutually exclusive group in another, or there's an attempt to add an argument group to either kind of group.

It still needs and argparse.rst changes

I'm raising a ValueError, since that is what most of the other add_argument errors do.  An alternative is a NotImplementedError, since that is, in effect, what I am doing, blocking the implementation of particular 'add' methods.

An alternative to adding this patch as high priority bug issue, is to include it in the UsageGroup patch (11588) which will implement nestable groups.
msg224010 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2014-07-25 22:52
ArgumentGroups and MutuallyExclusiveGroups, as currently defined, won't give you that kind of usage.  I have appended a script that uses UsageGroups, which I am developing for, 
to solve this.

It defines 2 'mxg' groups (groups with the xor logic of mutually exclusive groups), and 1 'any' group.  They can be nested.

The resulting usage line is:

    usage: PROG [-h] [[--opt1 | --opt2 | --opt3] | [--opt4 --opt5 --opt6]]

Normally '|' is used for simple logical 'or'.  But in mutually exclusive groups it denotes 'xor'.  So what should join 'any' lists?  You chose ' ', I was using ','.  Defining a usage notation that is simple, intuitive, and also flexible, is not easy.
msg224020 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2014-07-26 06:05
This patch adds a 

    class TestMutuallyExclusiveGroupErrors
        test_invalid_add_group() test,

closely modeled on


I'm using ValueError in group add methods (maintaining the similarity with add_argument errors).

I haven't changed the documentation.  add_argument_group and add_mutually_exclusive_group methods are described as belonging to an ArgumentParser, and the examples are consistent with that. An admonition against nesting groups would not fit with the current flow.

However to be accurate, these methods belong to _ActionsContainer, the parent class for both the parser and groups.  The documentation glosses over this detail.  So an alternative way of addressing this issue is to move these 2 methods to the ArgumentParser class.
msg286584 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2017-02-01 02:34
A similar issue about nesting an Argument Group inside a Mutually Exclusive Group.
Date User Action Args
2017-02-01 02:34:00paul.j3setmessages: + msg286584
2014-07-26 06:05:38paul.j3setfiles: + issue22047_2.patch

messages: + msg224020
2014-07-25 22:52:22paul.j3setfiles: +

messages: + msg224010
2014-07-25 21:37:22paul.j3setfiles: + issue22047_1.patch
keywords: + patch
messages: + msg224004
2014-07-25 17:37:52Sam.Kerrsetmessages: + msg223978
2014-07-25 17:28:34Sam.Kerrsetmessages: + msg223974
2014-07-25 17:24:55paul.j3setmessages: + msg223972
versions: + Python 3.5
2014-07-25 03:50:46paul.j3setnosy: + paul.j3
messages: + msg223917
2014-07-23 16:03:44Sam.Kerrcreate