Can Python's optparse display the default value of an option? - python

Is there a way to make Python's optparse print the default value of an option or flag when showing the help with --help?

Try using the %default string placeholder:
# This example taken from http://docs.python.org/library/optparse.html#generating-help
parser.add_option("-m", "--mode",
default="intermediate",
help="interaction mode: novice, intermediate, "
"or expert [default: %default]")

And if you want to add default values automatically to all options that you have specified, you can do the following:
for option in parser.option_list:
if option.default != ("NO", "DEFAULT"):
option.help += (" " if option.help else "") + "[default: %default]"

And if you need programmatic access to the default values, you can get to them via the defaults attribute of the parser (it's a dict)

The comments to your question already indicate there's another way to parse arguments called argparse. It's been introduced in Python 3.2. It actually deprecates optparse but is used similarly.
argpass comes with different formatting classes and for instance argparse.ArgumentDefaultsHelpFormatter will also print the default values without you having to manipulate the help string manually.
ArgumentParser objects allow the help formatting to be customized by
specifying an alternate formatting class. Currently, there are four
such classes:
class argparse.RawDescriptionHelpFormatter
class argparse.RawTextHelpFormatter
class argparse.ArgumentDefaultsHelpFormatter
class argparse.MetavarTypeHelpFormatter
An example from the python docs:
>>> parser = argparse.ArgumentParser(
... prog='PROG',
... formatter_class=argparse.ArgumentDefaultsHelpFormatter)
>>> parser.add_argument('--foo', type=int, default=42, help='FOO!')
>>> parser.add_argument('bar', nargs='*', default=[1, 2, 3], help='BAR!')
>>> parser.print_help()
usage: PROG [-h] [--foo FOO] [bar [bar ...]]
positional arguments:
bar BAR! (default: [1, 2, 3])
optional arguments:
-h, --help show this help message and exit
--foo FOO FOO! (default: 42)
see argparse formatting classes

Add argparse.ArgumentDefaultsHelpFormatter to your parser
import argparse
parser = argparse.ArgumentParser(
description='Your application description',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
from documentation:
ArgumentDefaultsHelpFormatter automatically adds information about
default values to each of the argument help messages:
Blockquote

Related

Python: command-line arguments --foo and --no-foo

For parsing boolean command-line options using Python's built-in argparse package, I am aware of this question and its several answers: Parsing boolean values with argparse.
Several of the answers (correctly, IMO) point out that the most common and straightforward idiom for boolean options (from the caller's point of view) is to accept both --foo and --no-foo options, which sets some value in the program to True or False, respectively.
However, all the answers I can find don't actually accomplish the task correctly, it seems to me. They seem to generally fall short on one of the following:
A suitable default can be set (True, False, or None).
Help text given for program.py --help is correct and helpful, including showing what the default is.
Either of (I don't really care which, but both are sometimes desirable):
An argument --foo can be overridden by a later argument --no-foo and vice versa;
--foo and --no-foo are incompatible and mutually exclusive.
What I'm wondering is whether this is even possible at all using argparse.
Here's the closest I've come, based on answers by #mgilson and #fnkr:
def add_bool_arg(parser, name, help_true, help_false, default=None, exclusive=True):
if exclusive:
group = parser.add_mutually_exclusive_group(required=False)
else:
group = parser
group.add_argument('--' + name, dest=name, action='store_true', help=help_true)
group.add_argument('--no-' + name, dest=name, action='store_false', help=help_false)
parser.set_defaults(**{name: default})
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
add_bool_arg(parser, 'foo', "Do foo", "Don't foo", exclusive=True)
add_bool_arg(parser, 'bar', "Do bar", "Don't bar", default=True, exclusive=False)
That does most things well, but the help-text is confusing:
usage: argtest.py [-h] [--foo | --no-foo] [--bar] [--no-bar]
optional arguments:
-h, --help show this help message and exit
--foo Do foo (default: None)
--no-foo Don't foo (default: None)
--bar Do bar (default: True)
--no-bar Don't bar (default: True)
A better help text would be something like this:
usage: argtest.py [-h] [--foo | --no-foo] [--bar] [--no-bar]
optional arguments:
-h, --help show this help message and exit
--foo --no-foo Whether to foo (default: None)
--bar --no-bar Whether to bar (default: True)
But I don't see a way to accomplish that, since "--*" and "--no-*" must always be declared as separate arguments (right?).
In addition to the suggestions at the SO question mentioned above, I've also tried creating a custom action using techniques shown in this other SO question: Python argparse custom actions with additional arguments passed . These fail immediately saying either "error: argument --foo: expected one argument", or (if I set nargs=0) "ValueError: nargs for store actions must be > 0". From poking into the argparse source, it looks like this is because actions other than the pre-defined 'store_const', 'store_true', 'append', etc. must use the _StoreAction class, which requires an argument.
Is there some other way to accomplish this? If someone has a combination of ideas I haven't thought of yet, please let me know!
(BTW- I'm creating this new question, rather than trying to add to the first question above, because the original question above was actually asking for a method to handle --foo TRUE and --foo FALSE arguments, which is different and IMO less commonly seen.)
One of the answers in your linked question, specifically the one by Robert T. McGibbon, includes a code snippet from an enhancement request that was never accepted into the standard argparse. It works fairly well, though, if you discount one annoyance. Here is my reproduction, with a few small modifications, as a stand-alone module with a little bit of pydoc string added, and an example of its usage:
import argparse
import re
class FlagAction(argparse.Action):
"""
GNU style --foo/--no-foo flag action for argparse
(via http://bugs.python.org/issue8538 and
https://stackoverflow.com/a/26618391/1256452).
This provides a GNU style flag action for argparse. Use
as, e.g., parser.add_argument('--foo', action=FlagAction).
The destination will default to 'foo' and the default value
if neither --foo or --no-foo are specified will be None
(so that you can tell if one or the other was given).
"""
def __init__(self, option_strings, dest, default=None,
required=False, help=None, metavar=None,
positive_prefixes=['--'], negative_prefixes=['--no-']):
self.positive_strings = set()
# self.negative_strings = set()
# Order of strings is important: the first one is the only
# one that will be shown in the short usage message! (This
# is an annoying little flaw.)
strings = []
for string in option_strings:
assert re.match(r'--[a-z]+', string, re.IGNORECASE)
suffix = string[2:]
for positive_prefix in positive_prefixes:
s = positive_prefix + suffix
self.positive_strings.add(s)
strings.append(s)
for negative_prefix in negative_prefixes:
s = negative_prefix + suffix
# self.negative_strings.add(s)
strings.append(s)
super(FlagAction, self).__init__(option_strings=strings, dest=dest,
nargs=0, default=default,
required=required, help=help,
metavar=metavar)
def __call__(self, parser, namespace, values, option_string=None):
if option_string in self.positive_strings:
setattr(namespace, self.dest, True)
else:
setattr(namespace, self.dest, False)
if __name__ == '__main__':
p = argparse.ArgumentParser()
p.add_argument('-a', '--arg', help='example')
p.add_argument('--foo', action=FlagAction, help='the boolean thing')
args = p.parse_args()
print(args)
(this code works in Python 2 and 3 both).
Here is the thing in action:
$ python flag_action.py -h
usage: flag_action.py [-h] [-a ARG] [--foo]
optional arguments:
-h, --help show this help message and exit
-a ARG, --arg ARG example
--foo, --no-foo the boolean thing
Note that the initial usage message does not mention the --no-foo option. There is no easy way to correct this other than to use the group method that you dislike.
$ python flag_action.py -a something --foo
Namespace(arg='something', foo=True)
$ python flag_action.py --no-foo
Namespace(arg=None, foo=False)

Defining the order of the arguments with argparse - Python

I have the following command line tool:
import argparse
parser = argparse.ArgumentParser(description = "A cool application.")
parser.add_argument('positional')
parser.add_argument('--optional1')
parser.add_argument('--optional2')
args = parser.parse_args()
print args.positionals
The output of python args.py is:
usage: args.py [-h] [--optional1 OPTIONAL1] [--optional2 OPTIONAL2]
positional
however I would like to have:
usage: args.py [-h] positional [--optional1 OPTIONAL1] [--optional2 OPTIONAL2]
How could I have that reordering?
You would either have to provide your own help formatter, or specify an explicit usage string:
parser = argparse.ArgumentParser(
description="A cool application.",
usage="args.py [-h] positional [--optional1 OPTIONAL1] [--optional2 OPTIONAL2]")
The order in the help message, though, does not affect the order in which you can specify the arguments. argparse processes any defined options left-to-right, then assigns any remaining arguments to the positional parameters from left to right. Options and positional arguments can, for the most part, be mixed.
With respect to each other the order of positionals is fixed - that's why they are called that. But optionals (the flagged arguments) can occur in any order, and usually can be interspersed with the postionals (there are some practical constrains when allowing variable length nargs.)
For the usage line, argparse moves the positionals to the end of the list, but that just a display convention.
There have been SO questions about changing that display order, but I think that is usually not needed. If you must change the display order, using a custom usage parameter is the simplest option. The programming way requires subclassing the help formatter and modifying a key method.

Case insensitive argparse choices

Is it possible to check argparse choices in case-insensitive manner?
import argparse
choices = ["win64", "win32"]
parser = argparse.ArgumentParser()
parser.add_argument("-p", choices=choices)
print(parser.parse_args(["-p", "Win32"]))
results in:
usage: choices.py [-h] [-p {win64,win32}]
choices.py: error: argument -p: invalid choice: 'Win32' (choose from 'win64','win32')
Transform the argument into lowercase by using
type = str.lower
for the -p switch.
This solution was pointed out by chepner in a comment. The solution I proposed earlier was
type = lambda s : s.lower()
which is also valid, but it's simpler to just use str.lower.
Using lower in the type is nice way of doing this, if you don't mind loosing the case information.
If you want to retain the case, you could define a custom choices class. The choices needs two methods, __contains__ (for testing in), and iteration (to list the choices).
class mylist(list):
# list subclass that uses lower() when testing for 'in'
def __contains__(self, other):
return super(mylist,self).__contains__(other.lower())
choices=mylist(['win64','win32'])
parser = argparse.ArgumentParser()
parser.add_argument("-p", choices=choices)
print(parser.parse_args(["-p", "Win32"]))
# Namespace(p='Win32')
The help is:
usage: ipython [-h] [-p {win64,win32}]
optional arguments:
-h, --help show this help message and exit
-p {win64,win32}
To clarify #5gon12eder's answer, you need to include the "type" as another paramater to add_argument:
parser.add_argument("-p", choices=choices, type=str.lower)
This will make sure that the input is lowercased.
Make sure to NOT add parentheses after lower.
Keeping the case information would also be possible with a one liner:
type = lambda arg: {x.lower(): x for x in choices}[arg.lower()],
Where choices would be the same list as passed to the choices parameter.

How to use a Python keyword as an argument name for argparse?

lambda has a keyword function in Python:
f = lambda x: x**2 + 2*x - 5
What if I want to use it as a variable name? Is there an escape sequence or another way?
You may ask why I don't use another name. This is because I'd like to use argparse:
parser = argparse.ArgumentParser("Calculate something with a quantity commonly called lambda.")
parser.add_argument("-l","--lambda",help="Defines the quantity called lambda", type=float)
args = parser.parse_args()
print args.lambda # syntax error!
Script called with --help option gives:
...
optional arguments
-h, --help show this help message and exit
-l LAMBDA, --lambda LAMBDA
Defines the quantity called lambda
Because of that, I would like to stay with lambda as the variable name. Solutions may be argparse-related as well.
You can use dynamic attribute access to access that specific attribute still:
print getattr(args, 'lambda')
Better still, tell argparse to use a different attribute name:
parser.add_argument("-l", "--lambda",
help="Defines the quantity called lambda",
type=float, dest='lambda_', metavar='LAMBDA')
Here the dest argument tells argparse to use lambda_ as the attribute name:
print args.lambda_
The help text still will show the argument as --lambda, of course; I set metavar explicitly as it otherwise would use dest in uppercase (so with the underscore):
>>> import argparse
>>> parser = argparse.ArgumentParser("Calculate something with a quantity commonly called lambda.")
>>> parser.add_argument("-l", "--lambda",
... help="Defines the quantity called lambda",
... type=float, dest='lambda_', metavar='LAMBDA')
_StoreAction(option_strings=['-l', '--lambda'], dest='lambda_', nargs=None, const=None, default=None, type=<type 'float'>, choices=None, help='Defines the quantity called lambda', metavar='LAMBDA')
>>> parser.print_help()
usage: Calculate something with a quantity commonly called lambda.
[-h] [-l LAMBDA]
optional arguments:
-h, --help show this help message and exit
-l LAMBDA, --lambda LAMBDA
Defines the quantity called lambda
>>> args = parser.parse_args(['--lambda', '4.2'])
>>> args.lambda_
4.2
There is an argparse-specific way of dealing with this. From the documentation:
If you prefer to have dict-like view of the attributes, you can use
the standard Python idiom, vars().
Therefore, you should be able to write:
print vars(args)["lambda"] # No keyword used, no syntax error.
argparse provides destination functionality for arguments if the long option name is not the desired attribute name for the argument.
For instance:
parser = argparse.ArgumentParser()
parser.add_argument("--lambda", dest="function")
args = parser.parse_args()
print(args.function)

Reserved word as argument in argparse [duplicate]

lambda has a keyword function in Python:
f = lambda x: x**2 + 2*x - 5
What if I want to use it as a variable name? Is there an escape sequence or another way?
You may ask why I don't use another name. This is because I'd like to use argparse:
parser = argparse.ArgumentParser("Calculate something with a quantity commonly called lambda.")
parser.add_argument("-l","--lambda",help="Defines the quantity called lambda", type=float)
args = parser.parse_args()
print args.lambda # syntax error!
Script called with --help option gives:
...
optional arguments
-h, --help show this help message and exit
-l LAMBDA, --lambda LAMBDA
Defines the quantity called lambda
Because of that, I would like to stay with lambda as the variable name. Solutions may be argparse-related as well.
You can use dynamic attribute access to access that specific attribute still:
print getattr(args, 'lambda')
Better still, tell argparse to use a different attribute name:
parser.add_argument("-l", "--lambda",
help="Defines the quantity called lambda",
type=float, dest='lambda_', metavar='LAMBDA')
Here the dest argument tells argparse to use lambda_ as the attribute name:
print args.lambda_
The help text still will show the argument as --lambda, of course; I set metavar explicitly as it otherwise would use dest in uppercase (so with the underscore):
>>> import argparse
>>> parser = argparse.ArgumentParser("Calculate something with a quantity commonly called lambda.")
>>> parser.add_argument("-l", "--lambda",
... help="Defines the quantity called lambda",
... type=float, dest='lambda_', metavar='LAMBDA')
_StoreAction(option_strings=['-l', '--lambda'], dest='lambda_', nargs=None, const=None, default=None, type=<type 'float'>, choices=None, help='Defines the quantity called lambda', metavar='LAMBDA')
>>> parser.print_help()
usage: Calculate something with a quantity commonly called lambda.
[-h] [-l LAMBDA]
optional arguments:
-h, --help show this help message and exit
-l LAMBDA, --lambda LAMBDA
Defines the quantity called lambda
>>> args = parser.parse_args(['--lambda', '4.2'])
>>> args.lambda_
4.2
There is an argparse-specific way of dealing with this. From the documentation:
If you prefer to have dict-like view of the attributes, you can use
the standard Python idiom, vars().
Therefore, you should be able to write:
print vars(args)["lambda"] # No keyword used, no syntax error.
argparse provides destination functionality for arguments if the long option name is not the desired attribute name for the argument.
For instance:
parser = argparse.ArgumentParser()
parser.add_argument("--lambda", dest="function")
args = parser.parse_args()
print(args.function)

Categories

Resources