What I mean by multiple level arguments is something like svn help, after parsing the svn help part, the following word is considered argument to help the subcommand.
Is it possible to set this up with optparse?
According to the python docs, optparse is now considered as deprecated, and won't be developed further; therefore i would strongly suggest you to use the module argparse, whith which you can create "multiple level" arguments.
import argparse
parser = argparse.ArgumentParser()
# Init sub-command
parser_init = subparsers.add_parser('init', help='initialize the things')
parser_init.add_argument(...)
# Help sub-command
parser_help = subparsers.add_parser('help', help='help me!')
parser_help.add_argument(...)
argparse support sub-commands : http://docs.python.org/library/argparse.html#sub-commands
optparse is deprecated in favor of argparse since python 2.7
Related
I am using Argparse to parse shell input to my Python function.
The tricky part is that this script first reads in a file that partially determines what kind of arguments are available to Argparse (it's a JSON file containing criteria by which the user can specify what data to output).
But before these arguments are added to my parser, I would like to read in some arguments relating to the file reading itself. (e.g. whether to fix the formatting of the input file). Kinda like this:
test.py (fix_formatting=True, **more arguments added later)
When I try to run args = parser.parse_args() twice, after the initial input and after adding more keys, things fall apart: Argparse quite predictably complains that some of the user input are unrecognized arguments:. I thought I might use subparsers to that end.
So I tried variations of (following the example in the docs as best as I could):
def main():
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(help='sub-command help')
settingsparser = subparsers.add_parser('settings') #i want a subparser called 'settings'
settingsparser.add_argument('--fix_formatting', action='store_true') #this subparser shall have a --fix_formatting
Then I try to parse only the "settings" part like so:
settings=parser.parse_args(['settings'])
This seems to work. But then I add my other keys and things break:
keys=['alpha','beta','gamma','delta']
for key in keys:
parser.add_argument("--"+key, type=str, help="X")
args = parser.parse_args()
If I parse any input for any of the arguments from keys, Argparse complains that I make an invalid choice: [...] (choose from 'settings'). Now I don't understand why I have to choose from "settings"; the docs say that the parse
will only contain attributes for the main parser and the subparser that was selected by the command line (and not any other subparsers)
what is my error of understanding here?
and if this is the wrong approach, how would one go about parsing one bit of input before another bit?
Any help is much appreciated!
parse_args calls parse_known_args. This returns the args namesparse along with a list of strings (from sys.argv) that it could not process (extras). parse_args raises this error if this list is not empty.
https://docs.python.org/3/library/argparse.html#partial-parsing
Thus parse_known_args is useful if you want to parse some of the input.
sys.argv remains unchanged. Subsequent calls to a parser (whether it was the original one or not) use that again, unless you pass the extras.
I don't think subparsers help you here. They aren't meant for delayed or two stage parsing. I'd suggest playing with the documentation examples for subparsers first.
To the main parser, the subparsers look like
subparsers = parser.add_argument('cmd', choices=['select',...])
In other words, it adds a positional argument where the choices are the subparser names that you define. That may help you see why it expects you to name select. Positionals are normally required.
(there's an exception to this in recent versions, https://stackoverflow.com/a/22994500/901925)
I have an application using argparse which is broken by the latest versions of Python. I am no longer able to alter the defaults for sub-commands.
My app has various modules and an optional GUI. The GUI calls the modules via the sub-commands, and there is an ini file which may alter the argument defaults.
The GUI has created the parser and the sub-parsers, passing arguments as set by the GUI user. Options in the ini file may override the defaults in the sub-parsers.
This works in 2.7.6, but was broken by later releases due to an apparent change in argparse.
import argparse
# create the top-level parser
parser = argparse.ArgumentParser(prog='PROG')
parser.add_argument('--foo', action='store_true', dest='_foo')
subparsers = parser.add_subparsers(help='sub-command help')
# create the parser for the "a" command
parser_a = subparsers.add_parser('a', help='a help')
parser_a.add_argument('--bar', type=int, default=0, dest='_bar')
#use ini file to alter default
d_ini = {'_bar': '1'}
parser.set_defaults(**d_ini)
# parse some argument lists
print parser.parse_args(['a'])
In python 2.7.6 this prints as expected:
Namespace(_bar=1, _foo=False)
But in later releases, eg. 2.7.10 it prints
Namespace(_bar=0, _foo=False)
Am I using argparse incorrectly, because if this is a python bug it has persisted for a few releases now?
Do I need to process the ini file before adding the sub-parser defaults? This will be more cumbersome than my current approach as it will need to be done separately for each argument, and there are many. The python documentation for ArgumentParser.set_defaults explicitly states "Parser-level defaults can be particularly useful when working with multiple parsers", so it surprising if this facility has been compromised.
In this case, the parser defaults must be applied to the sub parser of interest. That is, in the code above, you should have:
# set defaults on parser_a
parser_a.set_defaults(**d_ini)
Setting defaults on parser does not set defaults on the sub parsers. Instead it sets the default values for top level (global) arguments. Why? This way it is possible for the program to take an option --bar, and for multiple subcommands a, b, c to also all take the option --bar with different defaults.
The former behaviour, in 2.7.6, which also appears in 3.3, should be considered a bug.
I'm using argparse with optional parameter, but I want to avoid having something like this : script.py -a 1 -b -a 2
Here we have twice the optional parameter 'a', and only the second parameter is returned. I want either to get both values or get an error message.
How should I define the argument ?
[Edit]
This is the code:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-a', dest='alpha', action='store', nargs='?')
parser.add_argument('-b', dest='beta', action='store', nargs='?')
params, undefParams = self.parser.parse_known_args()
append action will collect the values from repeated use in a list
parser.add_argument('-a', '--alpha', action='append')
producing an args namespace like:
namespace(alpha=['1','3'], b='4')
After parsing you can check args.alpha, and accept or complain about the number of values. parser.error('repeated -a') can be used to issue an argparse style error message.
You could implement similar functionality in a custom Action class, but that requires understanding the basic structure and operation of such a class. I can't think anything that can be done in an Action that can't just as well be done in the appended list after.
https://stackoverflow.com/a/23032953/901925 is an answer with a no-repeats custom Action.
Why are you using nargs='?' with flagged arguments like this? Without a const parameter this is nearly useless (see the nargs=? section in the docs).
Another similar SO: Python argparse with nargs behaviour incorrect
I have a problem with giving arguments to a function when using callback. I am new to the command line argument scripting in python so go easy on me. Here's my code:
from optparse import OptionParser
import urllib
def google(option, opt_str, value, parser):
print options.f_name
parser = OptionParser()
parser.add_option("-f", "--first", type="string", dest="f_name", help="Supply a first name to search", action="store")
parser.add_option("-l", "--last", type="string", dest="l_name", help="Supply a last name to search", action="store")
parser.add_option("-g", "--google", action="callback", callback=google)
(options, args) = parser.parse_args()
And can't seem to figure out why it wouldn't print out the user supplied input. I've looked at the doc for python on optparse and it just gets fuzzy. Anyways any possibly way I can use options.f_name in that function. I have been using something as such to put into the functions arguments to use.
first_name = options.f_name
Then would supply the function with one of the arguments that didn't work either.
Old post, but for posterity:
You need to specify the type of the argument in order to have optparse inject it into the value parameter:
def google(option, opt_str, value, parser):
print value
parser.add_option("-g", "--google", action="callback", callback=google, type="string")
Full example showing how to return the value to the parser for inclusion into options:
from optparse import OptionParser
def doSomethingWithValue(value):
return "baked beans and {}".format(value)
def google(option, opt_str, value, parser):
setattr(parser.values, option.dest, doSomethingWithValue(value))
parser = OptionParser()
parser.add_option("-g", "--google", action="callback", callback=google, type="string", dest="googleOption")
(options, args) = parser.parse_args()
print(options)
# ./script.py ==> {'googleOption': None}
# ./script.py -g spam ==> {'googleOption': 'baked beans and spam'}
optparse is hard to use. You should try docopt. http://docopt.org/
As a side note, urllib is hard to use too. Check out the requests module. http://docs.python-requests.org/en/latest/
The order of evaluation of the arguments is not specified, so the callback for -g may be called before optparse handles the -f option. The only way to do this during the parse is to make them both callbacks that are aware of each other, and only when the second argument is handled does it perform the behaviour you are looking for.
Is there any reason you can't just set a flag and handle it after the parse_args() is complete, then you will be sure all the arguments have been handled.
BTW: optparse is deprecated in favour of argparse.
If you check the documentation, you'll find out that you actually need to use:
parser.values.f_name
Of course you should make sure that you take the precautions for cases where it hasn't been defined yet.
How can I pass options without any argument and without passing any default argument?
For example:
./log.py --ipv4
parser.add_option("--ipv4", action="store_true", dest="ipv4")
See http://docs.python.org/2/library/optparse.html#handling-boolean-flag-options
While lajarre's answer is correct, it's important to note outparse is considered deprecated.
I suggest using the newer argparse module instead.
So your code would look like:
import argparse
parser = argparse.ArgumentParser(description='This is my description')
parser.add_argument('--ipv4', action='store_true', dest='ipv4')
Using -foo or --foo flags makes the arguement optional. See this documentation for more about optional arguments.
Edit: And here's the specific documentation for the add_argument method.
Edit 2: Additionally, if you wanted to accept either -foo or --foo you could do
parser.add_argument('-ipv4', '--ipv4', action='store_true', dest='ipv4')