As we get into more details with python command-line arguments, we have to remember the generic household name in the command line family; getopt
.
See, before we had the fancy argparse
, we had this swiss army knife of a package. If you are a 'regular-rish' C programmer, I bet this rings a bell. As a notable difference between the two, there is definitely more code involved. There are a few more differences that will come to light as we explore the same program from before. Here goes. As a little challenge, we are going to leave the project section out just to see how far we can get without the extra nudge. Remember the square program?
import getopt
import sys
from math import sqrt
"""
get the square but get the square root in case the argument 'root' is provided
"""
def usage():
"""
Show help for the CLI program
"""
print("python advanced_square.py --number <your_number> \n OR\n")
print("python advanced_square.py -n <your_number>\n")
print("To get the square root: python advanced_square.py -n <your_number> -r")
print("Example: get the square\n\tpython advanced_square.py -n 5")
def main():
try:
option, arguments = getopt.getopt(sys.argv[1:],"hn:r",["help","number=","root"])
except getopt.GetoptError as error:
print(error)
sys.exit()
# initialize variables for for loop
number = None
root_number = False
for opt, variable in option:
if opt in ("-h", "--help"):
usage()
elif opt in ("-n", "--number"):
number = int(variable)
elif opt in ('-r','--root'):
root_number = True
else:
usage()
sys.exit()
if root_number:
print(f"The square root of {number} = {sqrt(number)}")
else:
print(f"The square of {number} = {number* number} ")
if __name__ == '__main__':
main()
What this program does is simple. Get the square of a number parsed as a command line or get it's root if the --root
or -r
is parsed.
To run it.
# get the square of 5
python advanced_square.py -n 5
# get the square root of 5
python advanced_square.py -n 5 --root
Let's break this down.
Unlike grandson argparse
which just knows how to display helpful messages, in getopt
we do not get this right out of the box. So running python advanced_square.py
will simply give a user an error screen like so
Traceback (most recent call last):
File "advanced_square.py", line 48, in <module>
main()
File "advanced_square.py", line 43, in main
print(f"The square of {number} = {number* number} ")
TypeError: unsupported operand type(s) for *: 'NoneType' and 'NoneType'
we have to specify how we want this run. That's where our custom function usage
comes in. Telling the user, 'Hey, know what? You messed up'
Take a look at this line here:
option, arguments = getopt.getopt(sys.argv[1:],"hn:r",["help","number=","root"])
What getopt.getopt()
does is accept three arguments,
#Accept a list of command-line arguments excluding the program #name itself.
sys.argv[1:]
Accept short options. The reason why in your Unix-like terminal you can say ls -a
or ls --al
l to list contents of a directory including hidden files. -a
would be the short option in this instance
"hn:r"
Note how we show short options. Our program, advanced_square.py
accepts three short options, namely, h
for help, n
for the number whose square or square root we want and r
to specify whether it is indeed the number's root we need.
See this n:
? That little colon after letter n specifies that after we write n, we need the actual number parsed. Hence -n 5
or -n 29
and so on. The long option alternative, as you might have noticed, would be number=
. We add an equal sight to it to show that this option needs an argument to follow it, unlike its counterpart root
and help
We follow it up with r
to specify that the user can just write -n 5 -r
which would mean get me the root of 5. The long alternatives to this program would be :
python advanced_square.py --number 5 --root
The order does not really matter. Whether --number
comes after --root
or not is up to you. getopt
will know what to do. Isn't that cool?
Just note, however, that if you specify n
to be capable of receiving a number, then that is what must follow it. So you cannot do this
# wrong move
python advanced_square.py --number --root 5
As a whole;
getopt.getopt(sys.argv[1:],"hn:r",["help","number=","root"])
The return of the above we get two items. The first, which we name 'options' looks like this [('-n', '5')]
while the second, named 'arguments' is simply this []
, an empty list! That's odd. One might say.
The arguments variable holds what extra arguments have been parsed to the program. So if a user does:
python advanced_square.py -n 7 9
options will look like:[('-n', '5')]
while arguments will look like ['9']
. It would hold all those weird extras you might pass in.So if we randomly decide to use:
python advanced_square.py -n 7 -h
options
would look like this [('-n', '7'), ('-h', '')]
, a list of tuples. See some light at the end of the tunnel?
Let's move to the next line.
getopt.GetoptError as error:
Notice how we get the error.
In this way, anything that is not in the required command-line argument specification is caught as an error. For instance, you use python advanced_square.py --animal 5
. In such a case, we want to display the error message and gracefully exit the program. In short, using the phrase animal
is nowhere defined in our program!
Because our main focus is on the arguments the program actually needs, we abandon use on the arguments
(extras) variable and loop via the list of the options
. We are saying, if ('-n', '7')
, in this case, n is present and has value, take it and do '1 2 3' else, if ('h', '')
which is present is there, call this function - help me!.
We convert the value of the parsed item, our '5' to an integer as getopt
assumes everything coming in is a string. So we change the input to an int
and assign it to a variable called 'number'
number = int(variable)
Getting the number and the value of root
as either True or False, we can safely get our square and root for use through the program.
This walk through's code, as usual, is at TheGreenCodes. Till next time though, adios!
Top comments (0)