12

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.

Community
  • 1
  • 1
rypel
  • 4,686
  • 2
  • 25
  • 36
  • 2
    have you considered [docopt](http://docopt.org/)? The disadvantage is that it allows option values to start with `-` [e.g., `"-f a -F -u"` (the format is "-u" here)](https://gist.github.com/zed/b94e3e9945debe25826d) – jfs May 28 '13 at 19:30
  • Is one of `-f` and `-u` required? If so, I'm picturing a solution involving subcommands or positional arguments. – chepner May 30 '13 at 13:02
  • 1
    @chepner: no, they aren't. i want the program to also run without args and options, so it will start a user dialog or even a GUI – rypel May 31 '13 at 09:42
  • @J.F.Sebastian: `docopt` looks great, i'll dig into it and report if it suits my needs – rypel May 31 '13 at 09:43

2 Answers2

5

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.


hpaulj
  • 221,503
  • 14
  • 230
  • 353
2

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
Jens Timmerman
  • 9,316
  • 1
  • 42
  • 48
  • 1
    `argparse` has built-in support for options with a variable number of arguments, as well as custom actions to process the argument(s) of a given option. However, I agree that in this case, it is simplest to just allow `-V` and ignore it if `-f` is used. – chepner May 30 '13 at 13:18
  • `optparse` is deprecated and `argparse` is its replacement. See: https://docs.python.org/3/library/argparse.html#upgrading-optparse-code – HEADLESS_0NE Mar 29 '16 at 18:41