13

I am trying to implement the following:

$ prog.py -h
usage: prog.py [-h] [-s | -m] [[-y [year]] | [[-1 | -3] [month] [year]]]

However, no matter how I played with add_argument_group and add_mutually_exclusive_group,

#!/usr/bin/env python

import argparse

def main(opt):
    print(opt)

if __name__ == '__main__':

    parser = argparse.ArgumentParser()
    bar = parser.add_mutually_exclusive_group()
    bar.add_argument('-s', action='store_true', default=True)
    bar.add_argument('-m', action='store_true', default=False)

    #bar = parser.add_argument_group()
    bar = parser.add_mutually_exclusive_group()
    bar.add_argument('-y', metavar='year', type=int,
                     dest='iy', nargs='?', default=0)
    baz = bar.add_argument_group()
    g_13 = baz.add_mutually_exclusive_group()
    g_13.add_argument('-1', dest='i1',
                      help='Display single month output.',
                      action='store_true', default=True)
    g_13.add_argument('-3', dest='i3',
                      help='Display prev/current/next month output.',
                      action='store_true', default=False)
    #aaa = bar.add_argument_group()
    baz.add_argument(metavar='month', type=int,
                        choices=range(1, 13),
                        dest='mo', nargs='?', default=1)
    baz.add_argument(metavar='year', type=int,
                        dest='yr', nargs='?', default=2000)

    main(parser.parse_args())

I can only manage:

$ prog.py -h
usage: prog.py [-h] [-s | -m] [-y [year]] [-1 | -3] [month] [year]

That is, I cannot group [-y [year]] and [[-1 | -3] [month] [year]] into a mutually exclusive group. I cannot figure out why. Could anyone help? Thanks.

Mr_and_Mrs_D
  • 32,208
  • 39
  • 178
  • 361
Ancora Imparo
  • 331
  • 1
  • 6
  • 20
  • Possible duplicate of http://stackoverflow.com/questions/17909294/python-argparse-mutual-exclusive-group – ronakg Jul 08 '14 at 05:23

1 Answers1

12

Argument groups just help organize the help display. They cannot be nested. Mutually exclusive groups test arguments and modify the usage display. They can be nested, but the end result is the same as if you made one big group. http://bugs.python.org/issue11588 is trying to create a more general purpose usage group.

In the mean time you can write a custom usage line, and test the arguments after parsing, if mutually exclusive groups don't give you enough control.


Here's a parser using code that I'm developing for http://bugs.python.org/issue11588

parser = argparse.ArgumentParser(formatter_class=argparse.UsageGroupHelpFormatter)

bar = parser.add_usage_group(kind='mxg', dest='s|m')
bar.add_argument('-s', action='store_true', default=True)
bar.add_argument('-m', action='store_true', default=False)

bar = parser.add_usage_group(kind='mxg', dest='year|more')
bar.add_argument('-y', metavar='year', type=int,...)

baz = bar.add_usage_group(kind='any', dest='', joiner=' ', parens='[]')

g_13 = baz.add_usage_group(kind='mxg', dest='1|3')
g_13.add_argument('-1', dest='i1',...)
g_13.add_argument('-3', dest='i3',...)

baz.add_argument(metavar='month', type=int,...)
baz.add_argument(metavar='year', type=int,...)

This replaces mutually_exclusive_group with usage_group which can be nested, and can test for other logical relations besides 'xor'. 'any' kind accepts any combination of its arguments, much as you expected the 'argument_group' to act.

The resulting usage is:

usage: prog.py [-h] [-s | -m] [-y [year] | [[-1 | -3] month year]] [month]
           [year]

The main fault is the display of the positionals, 'month' and 'year'. They are in the right place in the 'any' group, but they also display in the usual trailing location for positionals.

It accepts inputs like:

''
'-y 1943 -s
'-1 12 1943'
'12 1943'
'12'
'-3'

'1943' gives an error, because it it is out of range for a month

As you can see expanding the concept of groups is not trivial. I think I'm on the right track, but there are still rough edges.

hpaulj
  • 221,503
  • 14
  • 230
  • 353
  • I had thought add_mutually_exclusive_group is a special case of add_argument_group. Your add_usage_group seems the alternative to add_argument_group? – Ancora Imparo Jul 09 '14 at 02:29
  • MEGroup is a subclass of ArgumentGroup, but the overlap between the 2 classes is minimal. Their use is so different that they might as well be separate subclasses of the parent `_ActionsContainer` (which is also super for `ArgumentParser`). My `UsageGroup` is a generalization of the `MEGroup` idea. – hpaulj Jul 09 '14 at 04:21
  • Thanks for the clarifications. I agree that MEGroup and ArgumentGroup should be separate subclasses of _ActionContainer. Perhaps the next release of argparse in Python 3.5 will incorporate your implementation. Good job! – Ancora Imparo Jul 09 '14 at 05:11