I am using getopt to handle optional arguments in all my scripts. I have lots of scripts and i have to manage getopt in all the scripts separately because all scripts have different options.
My Question is is there any dynamic way to handle these options through single class by defining all options and just update those by objects ?
Currently i am using getopt like this:
import getopt
if __name__=="__main__":
if len(sys.argv) < 2:
usage()
else:
try:
options, remainder = getopt.getopt(sys.argv[2:], 's:n:i:a:c:t:p:', ['s1=','n1=','iteration=','api_v=', 'c='.....,])
except getopt.GetoptError:
print "\nError: Provided options are not correct!\n"
usage()
for opt, arg in options:
if opt in ('-s', '--s1'):
s1 = arg
if opt in ('-n', '--n1'):
try:
n1= int(arg)
except ValueError:
print("Error : n1 not an integer!")
exit()
elif opt in ('-i', '--iteration'):
try:
timeout = int(arg)
except ValueError:
print("Error : iteration not an integer!")
exit()
elif opt in ('-a', '--abc'):
abc = arg
.....
....
#and list goes on.
This i have in almost all my scripts with different options. Is there any cleaner way to achieve the same thing?
Look at the docopt module. It allows you to very very easily define options for your scripts with little to no code, just docstrings describing them: http://docopt.org/
You may find the argparse module easier to use and read. You could create an ArgumentParser factory function in a separate module and just call that from all your scripts to return a parser with the correct options
Related
following some examples from https://www.programcreek.com/python/example/121/getopt.getopt where they make option comparisons with == i.e (option == "--json"), I am trying to enter the if statement. However option will not match despite being of type string and "--json". Can anyone see what is going wrong?
import sys, getopt
argv = sys.argv[1:]
def main(argv):
try:
opts, args = getopt.getopt(argv,'',['json ='])
except:
print("failed to get arguments")
for (option, value) in opts:
print(option)
print(type(option))
if str(option) == "--json":
print ("enters here")
main(argv)
$ python test.py --json data
--json
<class 'str'>
You are using the longopts parameter incorrectly. Using an = in the option indicates that the option takes arguments but you are not passing your data argument to the --json option, you are passing it to the script as a global argument (because you omitted the = from the --json option).
Note also that you must remove the whitespace between the option and its argument. Finally, you should never use a bare except.
def main(argv):
try:
opts, args = getopt.getopt(argv, "", ["json="])
print(opts)
except Exception:
print("failed to get arguments")
for (option, value) in opts:
print(option)
print(type(option))
if str(option) == "--json":
print("enters here")
Usage:
$ python test.py --json='{"firstName":"John", "lastName":"Doe"}'
[('--json', '{firstName:John, lastName:Doe}')]
--json
<class 'str'>
enters here
Somewhere things stopped working. The intent was to show current disk usage if no argument given, show the directory's usage if provided as an argument, and show all subdirectories' disk usage if an argument called --all was given. Here is what I got so far. For some reason it fails now when supplying the "-d" for directory.
#!/usr/bin/env python
import os
import sys
import subprocess
import getopt
from humanize import naturalsize
def get_size(start_path = '.'):
total_size = 0
for dirpath, dirnames, filenames in os.walk(start_path):
for f in filenames:
fp = os.path.join(dirpath, f)
total_size += os.path.getsize(fp)
return total_size
def get_immediate_subdirectories(a_dir):
return [name for name in os.listdir(a_dir)
if os.path.isdir(os.path.join(a_dir, name))]
def version():
print ("Version: 1.0")
def usage():
print ("Usage: disk_usage.py [-a|--all][-n|--ncdu][-v|--version][directory name]")
def main():
try:
opts, args = getopt.getopt(sys.argv[1:], "adhnv", ["all","directory","help","ncdu","version",])
except getopt.GetoptError as err:
# print help information and exit:
print(err) # will print something like "option -a not recognized"
usage()
sys.exit(2)
for o, a in opts:
if o in ("-a", "--all"):
for subdir in get_immediate_subdirectories('.'):
print ("%s\t\t %s" % (naturalsize(get_size(subdir)),subdir))
sys.exit()
elif o in ("-d", "--directory"):
print (naturalsize(get_size(start_path=sys.argv[1:])))
elif o in ("-v", "--version"):
print (version())
sys.exit()
elif o in ("-h", "--help"):
usage()
sys.exit()
elif o in ("-n", "--ncdu"):
os.system('ncdu')
sys.exit()
else:
print (usage())
main()
I see a couple of problems with your usage of getopt. One (which sounds like you've figured-out for yourself) is that you're giving the wrong thing to get_size() by passing it start_path=sys.argv[1:] which assign start_path to a list of all the command arguments after the first.
The second issue is you're not defining the second and third arguments passed to getopt() properly. Quoting from the PyMOTW3:
The second argument is the option definition string for single character options. If one of the options requires an argument, its letter is followed by a colon.
(emphasis mine)
This means that in order to accept a value along with the -d option, you'd need to use "ad:hnv" instead of the "adhnv" you've got.
Similarly, for the --directory option in the list of long-style options:
The third argument, if used, should be a sequence of the long-style option names. Long style options can be more than a single character, such as --noarg or --witharg. The option names in the sequence should not include the “--” prefix. If any long option requires an argument, its name should have a suffix of “=”.
To support it there too requires you to pass ["all", "directory=", "help", "ncdu", "version"].
Unfortunately getopt doesn't support having an optional option argument. The reason is likely because it makes parsing ambiguous: For example, if -a takes an optional argument and -b is another option entirely, it unclear how interpret -ab.
If you can live with that limitation, the following incorporates changes to address the above issues:
import os
import sys
import subprocess
import getopt
from humanize import naturalsize
def get_size(start_path='.'):
# Might was to check validity of path here...
#if not (os.path.exists(start_path) and os.path.isdir(start_path)):
# raise ValueError('Invalid path argument: {!r}'.format(start_path))
total_size = 0
for dirpath, dirnames, filenames in os.walk(start_path):
for f in filenames:
fp = os.path.join(dirpath, f)
total_size += os.path.getsize(fp)
return total_size
def get_immediate_subdirectories(a_dir):
return [name for name in os.listdir(a_dir)
if os.path.isdir(os.path.join(a_dir, name))]
def version():
print("Version: 1.0")
def usage():
print("Usage: disk_usage.py [-a|--all][-n|--ncdu][-v|--version][directory name]")
def main():
try:
opts, args = getopt.getopt(sys.argv[1:], "ad:hnv",
["all", "directory=", "help", "ncdu", "version",])
except getopt.GetoptError as err:
print(err)
usage()
if __name__ == '__main__':
return 2 # Unit test.
else:
sys.exit(2)
for opt, arg in opts:
if opt in ("-a", "--all"):
for subdir in get_immediate_subdirectories('.'):
print("%s\t\t %s" % (naturalsize(get_size(subdir)), subdir))
sys.exit()
elif opt in ("-d", "--directory"):
print(naturalsize(get_size(start_path=arg)))
elif opt in ("-v", "--version"):
print(version())
sys.exit()
elif opt in ("-h", "--help"):
usage()
sys.exit()
elif opt in ("-n", "--ncdu"):
os.system('ncdu')
sys.exit()
else:
print(usage())
else:
print(usage())
if __name__ == '__main__':
# Unit test.
sys.argv = ['disk_usage.py'] # no option given
main()
sys.argv = 'disk_usage.py -d.'.split() # short opt
main()
sys.argv = 'disk_usage.py -dsome_folder'.split() # short opt
main()
sys.argv = 'disk_usage.py -d some_folder'.split() # short opt
main()
sys.argv = 'disk_usage.py --directory some_folder'.split() # long opt
main()
sys.argv = 'disk_usage.py -d'.split() # Missing argument error
main()
I think I got it now. Line 42 included sys.argv[1:] which returned a list. It should actually be sys.argv[2] to reference the directory after supplying the -d option.
There are two problems here.
The first problem is that you're using getopt wrong:
opts, args = getopt.getopt(sys.argv[1:], "adhnv", ["all","directory","help","ncdu","version",])
This returns a list of options opts, and a list of args with the options filtered out. But you're continuing to use sys.argv instead of args:
print (naturalsize(get_size(start_path=sys.argv[1:])))
In an invocation like myprogram -d spam, sys.argv[1:] is going to be ['-d', 'spam']. But you don't want the -d. That's the whole reason you called getopts in the first place. So, use args here, not sys.argv.
The second problem is that you're taking a slice, sys.argv[1:], but treating it as if it were a single value, rather than a list of values.
If you want to allow multiple directories to be passed with -d and to work on all of them, you need a for arg in …: loop over the slice.
If you want just a single directory, then you need to just index the list, like [1], not [1:]. Although, if you want that, you probably don't want -d to be a boolean flag and the directory to be an arg; you want -d to be a flag with the directory as its value.
More generally, you probably shouldn't be using getopt here in the first place. As the docs say:
Note 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.
If you're a getopt wizard, then sure, use getopt for simple projects instead of learning something new, even if it's better. But for anyone else, there is really no good reason to learn getopt in the first place, much less to learn how to debug all the funky corners of it. argparse is just plain better in every way. And if you don't like argparse, there are half a dozen alternatives on PyPI.
import pywaves as pw
import sys, getopt
amount = 0
receive = ''
try:
options, remainder = getopt.getopt(
sys.argv[1:],
'r:a',
['receive',
'amount',
])
except getopt.GetoptError as err:
print('ERROR:', err)
sys.exit(1)
for opt, arg in options:
if opt in ('-a', '--amount'):
amount = arg
elif opt in ('-r', '--receive'):
receive = arg
print('OPTIONS :', options)
myAddress = pw.Address(privateKey='MYPRIVATEKEY')
otherAddress = pw.Address(receive)
myToken = pw.Asset('MYADDRESS')
myAmount = amount
myAddress.sendAsset(otherAddress, myToken, myAmount)
I tried run the code above and it seems my option "a" was not captured. What should I do to get it working?
I run the following command line
python this.py -r 3PFPovgPu3aBWA1krU544tPDTFiHgpvu7q1 -a 150
It returns
('OPTIONS :', [('-r', '3PFPovgPu3aBWA1krU544tPDTFiHgpvu7q1'), ('-a', '')])
I not sure why the "a" value was empty. How do I change my code to make it work properly?
You need to add a colon after the second parameter 'a'. So try
getopt.getopt(sys.argv[1:],'r:a:',['receive','amount'])
See the documentation for getopt, there it is said clearly:
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).
This question already has answers here:
How to read/process command line arguments?
(22 answers)
Closed 6 years ago.
I finished writing a script in python, and now stuck in the interface, which requires getting few options from user but not sure what is the best way to get optional arguments..
The code for that is as below...
def getOptions(argv):
try:
opts,args = getopt.getopt(argv, "hi:c:d:m", ["ifile=", "add=", "delete"])
except getopt.GetoptError:
printUsage()
sys.exit(2)
for opt, arg in opts:
if opt in ("-h", "--help"):
print ("test -m <make> [src] [dst]\n")
print ("test -i <install>[filename] \n")
.....
sys.exit()
if opt in ( "-m", "--make"):
make(arg)
sys.exit()
if opt in ("-i","--install"):
install(arg)
sys.exit()
... # few more options
else:
assert False, "unhandled option"
My question is that how can i leave out the argument (like use a default optional path for arg) and if that is not provided, get from user ??
Currently i've to provide
./test -i
how can i leave out file name and call like
./test -i
I'm stuck with same issue one more time, My development environment is python 2.6 and because of the limitations, now using optparse but with same issue, i've to support an optional argument and couldn't find way how to do that other than parsing sys.argv manually, i've almost 10 different options and if i can handle one optional argument, my script would be much more convenient for end users.
From:
https://docs.python.org/2/library/getopt.html
Getopts does not support optional arguments.
Can you trying writing your code using argparse? Following is an example:
import argparse
parser = argparse.ArgumentParser(description='python cli')
parser.add_argument("-m", "--make", help="execute make", required=True)
parser.add_argument("-i", "--install", help="execute install", required=True)
# parse input arguments
args = parser.parse_args()
makeVal = args.make
shouldInstall = args.install
if makeVal :
make(makeVal)
...
Reference:
https://docs.python.org/2.7/library/argparse.html
Can anyone spot why the following script is not printing the passed arguments?
import sys, getopt
def usage():
print 'Unknown arguments'
def main(argv):
try:
opts, args = getopt.getopt(argv,'fdmse:d',['files=','data-source=','mode=','start','end'])
except getopt.GetoptError:
usage()
sys.exit(999)
for opt, arg in opts:
# print opt,arg
if opt in('-f','--files'):
print 'files: ', arg #
if __name__ == "__main__":
main(sys.argv[1:])
When I run the script at the command line and pass the arguments -f=dummy.csv, usage() seems to be invoked instead - WHY?
BTW, I find the logic of the program flow a bit weird (I copied it from here). Normally, I would have thought that the logic will be implemented in the try branch, and then AFTER that comes the exception handler.
Is this (as pasted in the code above) the 'Pythonic' way to write try/catch blocks?
Did you get your answers?
One way to debug python exceptions is to move (or copy temporarily for debugging) the code out of the try block. You'll get a full trace.
And of course another way is to reduce the test case. Here I've reduced the problems to three lines, and tried the solution hinted at by #s.lott (using 'f:' in the getopts call), and also show at the end how calling with some different test data behaves:
$ cat x1.py
import sys, getopt
opts, args = getopt.getopt(sys.argv[1:],'fdmse:d',['files=','data-source=','mode=','start','end'])
print "opts=", opts, "args=", args
$ python x1.py -f=dummy.csv argblah
Traceback (most recent call last):
File "x1.py", line 2, in <module>
opts, args = getopt.getopt(sys.argv[1:],'fdmse:d',['files=','data-source=','mode=','start','end'])
File "/usr/lib/python2.6/getopt.py", line 91, in getopt
opts, args = do_shorts(opts, args[0][1:], shortopts, args[1:])
File "/usr/lib/python2.6/getopt.py", line 191, in do_shorts
if short_has_arg(opt, shortopts):
File "/usr/lib/python2.6/getopt.py", line 207, in short_has_arg
raise GetoptError('option -%s not recognized' % opt, opt)
getopt.GetoptError: option -= not recognized
$ sed 's/fdm/f:dm/' <x1.py >x2.py
$ diff x1.py x2.py
2c2
< opts, args = getopt.getopt(sys.argv[1:],'fdmse:d',['files=','data-source=','mode=','start','end'])
---
> opts, args = getopt.getopt(sys.argv[1:],'f:dmse:d',['files=','data-source=','mode=','start','end'])
$ python x2.py -f=dummy.csv argblah
opts= [('-f', '=dummy.csv')] args= ['argblah']
$ python x1.py -f dummy.csv argblah
opts= [('-f', '')] args= ['dummy.csv', 'argblah']
Normally, I would have thought that the logic will be implemented in the try branch
"Normally"? What does normally mean?
What is the program supposed to do? What exceptions make sense? What does the program do in response to the exceptions.
There's no "normally". Any more than there's a normal assignment statement or a normal function definition.
Your program does what makes sense to achieve the required end-state. There's no "normally".
Import and use argparse instead of getopt. It's much easier to use and has almost all you need for running from the command line, built into it.
An example,
parser = argparse.ArgumentParser(
description='Description of what the module does when run.')
parser.add_argument("-o", "--output", help='Path of log file.')
args = parser.parse_args()
As easy as that. You need to import argparse at the top of your file for this to work, of course.