user3872776
user3872776

Reputation: 55

Commandline options with optional arguments using optparse

I'm looking for a way to make a commandline option with optional argument using optparse Python module.

For example there are two command lines: foobar -u -v and foobar -u USERNAME -v. While latter gets USERNAME as an argument, former should detect absence of an argument and get username from the environment variable or by other means, and not treat -v as an argument, but as an option instead.

I know there is possibility to take multiple arguments using append action, but it requires at least one argument.

Are there any solutions?

UPDATE:

Why won't this script works if no arugment is provided ? I have given some value to Default also. Cannot something like this can used ?

#!/usr/bin/env python                                                                                                                                                             
from optparse import OptionParser

parser = OptionParser()
parser.add_option("-u", "--user", dest="in_dir",
                  help="write report to FILE", action= "store", default='USERNAME')

(options, args) = parser.parse_args()
if options.in_dir == 'USERNAME':
        print' argument supplied or taken from default '
else:
        print 'arugment supplied'

On executing this file we get ./options.py -f Usage: options.py [options]

options.py: error: -f option requires an argument

As I have given some values in the default. Why cannot it take that value from there ? Is any there other solution to this?

Upvotes: 1

Views: 3358

Answers (3)

saraedum
saraedum

Reputation: 1529

There is an example in the optparse documentation that does something similar through a callback.

If the ambiguities are easy to sort out, e.g., because the optional argument is an int, you can use the following that is essentially copied over from SageMath's sage-runtest script.

import optparse

def optional_argument(option, opt_str, value, parser, typ, default_arg):
    try:
        next_arg = typ(parser.rargs[0])
    except Exception:
        next_arg = default_arg
    else:
        parser.rargs.pop(0)
    setattr(parser.values, option.dest, next_arg)

parser = optparse.OptionParser()
parser.add_option('-n',
    '--number',
    dest='n',
    default=-1,
    action="callback",
    callback=optional_argument,
    callback_args=(int, -2),
    nargs=0)

With this you get:

>>> parser.parse_args()
(<Values at ...: {'n': -1}>, [])
>>> parser.parse_args(['-n'])
(<Values at ...: {'n': -2}>, [])
>>> parser.parse_args(['-n', 42])
(<Values at ...: {'n': 42}>, [])

Note that you would need to explicitly mention the optional parameter in the documentation; optparse, renders the help as if this parameter took no arguments:

>>> parser.print_help()
...
Options:
  -h, --help    show this help message and exit
  -n, --number  

Upvotes: 0

loluengo
loluengo

Reputation: 76

Maybe you could inspect sys.argv before adding the -u option to your parser. If the next element on sys.argv (following -u) is not a username, then you could insert the username into the list before parsing. Something like the following code :

import sys
from optparse import OptionParser as OP
cmdLine = sys.argv
i = cmdLine.index('-u')
if (i+1) == len(cmdLine) or cmdLine[i+1] not in users:
    cmdLine.insert(i+1,userName)
p = OP()
p.add_option('-u',action='append')
p.parse_args(cmdLine[1:])

Upvotes: 1

Serge Ballesta
Serge Ballesta

Reputation: 148900

AFAIK there is no solution with the optparse module. Extract from The Python Standard Library (same for Python 2 2 and 3):

Typically, a given option either takes an argument or it doesn’t. Lots of people want an “optional option arguments” feature, meaning that some options will take an argument if they see it, and won’t if they don’t. This is somewhat controversial, because it makes parsing ambiguous: if -a takes an optional argument and -b is another option entirely, how do we interpret -ab? Because of this ambiguity, optparse does not support this feature.

You could have more luck with the newer argparse module that explicitely support that kind of requirement. Extracts from The Python Standard Library for argparse

The add_argument() method

ArgumentParser.add_argument(name or flags...[, action][, nargs][, const][, default][, type][, choices][, required][, help][, metavar][, dest])
Define how a single command-line argument should be parsed. Each parameter has its own more detailed description below, but in short they are:
...
nargs - The number of command-line arguments that should be consumed.
...

and later in nargs description :

'?'. One argument will be consumed from the command line if possible, and produced as a single item. If no command-line argument is present, the value from default will be produced.

Upvotes: 0

Related Questions