For a script, I'm currently using OptionParser to add variables to an input. However, all of my current options are booleans, and it seems it would just be easier to parse using argv instead. For example:
$ script.py option1 option4 option6
And then do something like:
if 'option1' in argv:
do this
if 'option2' in argv:
do this
etc...
Would it be suggested to use argv over OptionParser when the optionals are all booleans?
"However, all of my current options are booleans, and it seems it
would just be easier to parse using argv instead."
There's nothing wrong with using argv, and if it's simpler to use argv, there's no reason not to.
OptionParser has been deprecated, and unless you're stuck on an older version of python, you should use the ArgParser module.
For one-off scripts, there's nothing wrong with parsing sys.argv yourself. There are some advantages to using an argument parsing module instead of writing your own.
Standardized. Do you allow options like "-test", because the standard is usually 2 underscores for multichar options (e.g. "--test"). With a module, you don't have to worry about defining standards because they're already defined.
Do you need error-catching and help messages? Because you get a lot of that for free with ArgParse.
Will someone else be maintaining your code? There's already lots of documentation and examples of ArgParse. Plus, it's somewhat self documenting, because you have to specify the type and number of arguments, which isn't always apparent from looking at a sys.argv parser.
Basically, if you ever expect your command line options to change over time, or expect that your code will have to be modified by someone else, the overhead of ArgParse isn't that bad and would probably save you time in the future.
Related
I want to log the usage of a python program which uses the argparse module. Currently, the logger records the command line usage similar to the answer given in this post. However, that only gives the command line arguments, and not including the defaults set later in argparse (which is the intended use, after all). Is there a simple way to print ALL the argparse options to create a nice, tidy usage log entry that includes the default values?
It isn't difficult to go into the argparse namespace to fetch each argument by name, but I am hoping that someone has a concise way of extracting the needed info.
In response to the accepted answer:
Great! If anyone is interest, here is my implementation with logging:
logger.info("Usage:\n{0}\n".format(" ".join([x for x in sys.argv])))
logger.debug("All settings used:") for k,v in sorted(vars(args).items()):
logger.debug("{0}: {1}".format(k,v))
I've done something similar in an application, hopefully the below snippets give you what you need. It is the call to vars that gave me a dictionary of all the arguments.
parser = argparse.ArgumentParser(description='Configure')
....
args = parser.parse_args()
......
options = vars(args)
My application parses the command line arguments:
import sys
import getopt
arguments = sys.argv[1:]
options, remainder = getopt.getopt(arguments, "aa:bb:cc:dd:h", ["aaaa=", "bbbb=", "cccc=", "dddd=", "help"])
print dict(options)
This works great but at the same time odd: if I put the arguments in the different order, they aren't get parsed
python my_app.py --aaaa=value1 --bbbb=value2 --cccc=value3 --dddd=value4 #ok
python my_app.py --dddd=value4 --bbbb=value2 --cccc=value3 --aaaa=value1 # empty
That's disappointing because the order of the arguments shouldn't matter, should it? Is there any way to solve that?
UPDATE:
python my_app.py -aa value1 # odd, empty { "-a" : "" }
python my_app.py -a value1 # even this empty { "-a" : "" }
As stated in the first comment to your question, your main example regarding failed parsing of arguments in a different order works just fine:
~/tmp/so$ python my_app.py --aaaa=value1 --bbbb=value2 --cccc=value3 --dddd=value4
{'--aaaa': 'value1', '--cccc': 'value3', '--dddd': 'value4', '--bbbb': 'value2'}
~/tmp/so$ python my_app.py --dddd=value4 --bbbb=value2 --cccc=value3 --aaaa=value1
{'--cccc': 'value3', '--bbbb': 'value2', '--aaaa': 'value1', '--dddd': 'value4'}
If that's not the case for you, please update the script to print the remainder as well, and show its output.
However, you have still misused the getopt library and that's the reason the latest examples you provided don't work as expected. You can't specify more than a single character as an option, since the second character would count as a new separate option. getopt provides no way to differentiate between two consecutive characters that count as a single option (with the first one carrying no argument value, as it is not followed by a colon) or a single option that is composed of two characters. From getopt.getopt's documentation, with my added emphasis:
options is the string of option letters that the script wants to recognize, with options that require an argument followed by a colon.
Therefore, when getopt parses your arguments, each time it encounters a -a argument, it associates it with the first a option it notices, which in your case is not followed by a colon. Thus, it sets this option, discards its argument value, if there was any (if -aa was passed as an argument to the script, the second a counts as the argument value) and moves on to the next argument.
Finally, regarding getopt and argparse. The documentation clearly advocates argparse:
The getopt module is a parser for command line options whose API is designed to be familiar to users of the C getopt() function. Users who are unfamiliar with the C getopt() function or who would like to write less code and get better help and error messages should consider using the argparse module instead.
More about why argparse is better than both getopt and the deprecated optparse can be read in this PEP and in the answers to this question.
The only functionality that I've found to be supported in getopt while it requires a bit of work in argparse is argument order permutation like that of gnu getopt. However, this question explains how this can be achieved via argparse.
Is there a correct way to read the arguments to a python application?
Example:
python game.py -server 127.0.0.1 -nick TheKiller1337
Is there a correct way of interpreting these argument?
As it is now I have a while-loop with some ifs. But it is getting rather large. Should I do a general class for argument reading, or is this already implemented in python?
Use argparse, optparse or getopt.
All three are in the standard library.
I recommend argparse. It is the newest of the three, and is IMO the easiest to use. It was introduced in version 2.7.
If using an older Python version, I would recommend optparse (or get argparse for version 2.5 and 2.6 from pypi)
If you're using v2.7 or newer, you can use argparse. The documentation has examples.
For earlier Pythons, optparse is usually the way to go.
The alternative is getopt, if you're rather be writing 'C'.
For each of these you would have to change your argument list to more conventional. Either of:
python game.py --server 127.0.0.1 --nick TheKiller1337
python game.py -s 127.0.0.1 -n TheKiller1337
You can use getopt with only slight change to your initial plans. It would be like:
python game.py -s127.0.0.1 -nTheKiller1337
I prefer optparse, because it is supported in 2.6 and because it has a nice interface, automatically generates help texts, and supports additional parameters, not just arguments.
Like this:
from optparse import OptionParser
parser = OptionParser()
parser.add_option("-e", "--event", dest="type", help="type of EVENT")
(options, args) = parser.parse_args()
if options.type == 'fubar':
blah.blubb()
You get the idea.
I know it can be achieved by command line but I need to pass at least 10 variables and command line will mean too much of programming since these variables may or may not be passed.
Actually I have build A application half in vB( for GUI ) and Half in python( for script ). I need to pass variables to python, similar, to its keywords arguments, i.e, x = val1, y = val2. Is there any way to achieve this?
If you are using Python <2.7 I would suggest optparse.
optparse is deprecated though, and in 2.7 you should use argparse
It makes passing named parameters a breeze.
you can do something fun like call it as
thepyscript.py "x = 12,y = 'hello world', z = 'jam'"
and inside your script,
parse do:
stuff = arg[1].split(',')
for item in stuff:
exec(item) #or eval(item) depending on how complex you get
#Exec can be a lot of fun :) In fact with this approach you could potentially
#send functions to your script.
#If this is more than you need, then i'd stick w/ arg/optparse
Since you're working on windows with VB, it's worth mentioning that IronPython might be one option. Since both VB and IronPython can interact through .NET, you could wrap up your script in an assembly and expose a function which you call with the required arguments.
Have you taken a look at the getopt module? It's designed to make working with command line options easier. See also the examples at Dive Into Python.
If you are working with Python 2.7 (and not lower), than you can also have a look at the argparse module which should make it even easier.
If your script is not called too often, you can use a configuration file.
The .ini style is easily readable by ConfigParser:
[Section_1]
foo1=1
foo2=2
foo3=5
...
[Section_2]
bar1=1
bar2=2
bar3=3
...
If you have a serious amount of variables, it might be the right way to go.
What do you think about creating a python script setting these variables from the gui side? When starting the python app you just start this script and you have your vars.
Execfile
If I'm using this with getopt:
import getopt
import sys
opts,args = getopt.getopt(sys.argv,"a:bc")
print opts
print args
opts will be empty. No tuples will be created. If however, I'll use sys.argv[1:], everything works as expected. I don't understand why that is. Anyone care to explain?
The first element of sys.argv (sys.argv[0]) is the name of the script currently being executed. Because this script name is (likely) not a valid argument (and probably doesn't begin with a - or -- anyway), getopt does not recognize it as an argument. Due to the nature of how getopt works, when it sees something that is not a command-line flag (something that does not begin with - or --), it stops processing command-line options (and puts the rest of the arguments into args), because it assumes the rest of the arguments are items that will be handled by the program (such as filenames or other "required" arguments).
It's by design. Recall that sys.argv[0] is the running program name, and getopt doesn't want it.
From the docs:
Parses command line options and
parameter list. args is the argument
list to be parsed, without the leading
reference to the running program.
Typically, this means sys.argv[1:].
options is the string of option
letters that the script wants to
recognize, with options that require
an argument followed by a colon (':';
i.e., the same format that Unix
getopt() uses).
http://docs.python.org/library/getopt.html