Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python 2.7 argparse: How to nest optional mutally exclusive arguments properly?

My Program should include the following options, properly parsed by argparse:

  1. purely optional: [-h, --help] and [-v, --version]
  2. mutually exclusive: [-f FILE, --file FILE] and [-u URL, --url URL]
  3. optional if --url was chosen: [-V, --verbose]
  4. required if either --file or --url was chosen: [-F, --format FORMAT]

The desired usage pattern would be:

prog.py [-h] [-v] [-f FILE (-F FORMAT) | -u URL [-V] (-F FORMAT) ]

with the -F requirement applying to both members of the mutually exclusive group.
Not sure if it rather be a positional.

So it should be possible to run:

prog.py -u "http://foo.bar" -V -F csv

and the parser screaming in case i forgot the -F (as he's supposed to).

What i've done so far:

parser = ArgumentParser(decription='foo')

group = parser.add_mutually_exclusive_group()    
group.add_argument('-f','--file', nargs=1, type=str, help='')
group.add_argument('-u','--url', nargs=1, type=str, help='')    

parser.add_argument('-V','--verbose', action='store_true', default=False, help='')
parser.add_argument('-F','--format', nargs=1, type=str, help='')

Since it has a 'vanilla mode' to run without command line arguments, all arguments must be optional.

How can i implement points 3. and 4. into my code?

EDIT:
I tried -f and -u as subparsers, as described here, but subcommands seem to be treated like positionals and the parser gives me an error: too few arguments if i run it without arguments.

like image 708
rypel Avatar asked May 28 '13 10:05

rypel


2 Answers

Use of nargs=2 and tuple metavar approximates your goal

parser = argparse.ArgumentParser(prog='PROG')
group = parser.add_mutually_exclusive_group()
group.add_argument('-f','--file', nargs=2, metavar=('FILE','FORMAT'))
group.add_argument('-u','--url', nargs=2, metavar=('URL','FORMAT'))
parser.add_argument('-V','--verbose', action='store_true',help='optional with url')

which produces:

usage: PROG [-h] [-f FILE FORMAT | -u URL FORMAT] [-V]

optional arguments:
  -h, --help            show this help message and exit
  -f FILE FORMAT, --file FILE FORMAT
  -u URL FORMAT, --url URL FORMAT
  -V, --verbose         optional with url

This requires the format along with filename or url, it just doesn't require the -F. As others noted -V can be ignored in the -f case.


I tried -f and -u as subparsers, as described here, but subcommands seem to be treated like positionals and the parser gives me an error: too few arguments if i run it without arguments.

In the latest version(s) subcommands are no longer treated as required positionals. This was, as best I can tell, a side effect of changing the error message to be more informative. Instead of _parse_known_args doing a:

    if positionals:
        self.error(_('too few arguments'))

it scans _actions to see which are required, and then lists them by name in the error message. This is discussed in http://bugs.python.org/issue9253 . I know this change is in development (3.4), and may also be in 3.3.


like image 103
hpaulj Avatar answered Oct 22 '22 08:10

hpaulj


These points can enforced in optparse using a callback method when a certain option is present.

However, in argparse these are not available.

You can add a subparser for the url and the file sub-option, and parse these seperatly. from the help:

Note that the object returned by parse_args() will only contain attributes for the main parser and the subparser that was selected by the command line (and not any other subparsers). So in the example above, when the a command is specified, only the foo and bar attributes are present, and when the b command is specified, only the foo and baz attributes are present.

But I would just properly document the usage, and just ignore the arguments that are not applicable.

e.g. let these two command lines behave exactly the same:

prog.py -f FILE -V
prog.py -f FILE
like image 23
Jens Timmerman Avatar answered Oct 22 '22 08:10

Jens Timmerman