argparse
If you recall from the "Writing Scripts" page, we mentioned that ordering arguments is the easiest way to write your scripts. Unfortunately, this is limited in its functionality — other people might not know of your forced order and it can be complicated when many arguments are required.
argparse
is a Python package that solves our problems by creating consistent command-line interfaces and handling the arguments necessary to make your script function.
Coding Basics
We’ll begin by showing argparse
stripped down to its most basic level. The contents of this tutorial were inspired by Python’s argparse
tutorial and the argparse
documentation.
import argparse
def main():
parser = argparse.ArgumentParser()
args = parser.parse_args()
if __name__ == '__main__':
main()
Recall that the structure of |
As with any package, the first step is importing argparse
. Our parser
variable is an ArgumentParser
object, on which we add & parse arguments and ask for help. Assigning parser.parse_args()
to args
is the final step that checks for errors and converts our argument strings into objects. Declaring that variable is recommended to access the arguments for further use in the script.
If the above code was stored in a file called test.py
and then executed in the terminal, it wouldn’t yield anything. However, we can include the -h
/--help
flag to get some basic output:
$ python test.py -h
usage: test.py [-h] optional arguments: -h, --help show this help message and exit
The help flag is intrinsic to any script and is very useful for deciphering any script you use or write.
Arguments & Options
Let’s make our script more useful by adding some arguments before parsing:
import argparse
def main():
parser = argparse.ArgumentParser()
parser.add_argument('say', help='outputs the given string')
parser.add_argument('boom', help='prints the string in uppercase')
args = parser.parse_args()
print(args.say)
print(args.boom.upper())
if __name__ == '__main__':
main()
$ python test.py hey ya
hey YA
Let’s use --help
again to determine what we’re looking at:
$ python test.py --help
usage: test.py [-h] say boom positional arguments: say outputs the given string boom prints the string in uppercase optional arguments: -h, --help show this help message and exit
Cool! We confirm that say
and boom
are positional arguments, meaning that their position is specific and required. Our optional help
parameter of the add_argument
method is displayed in the help menu. If we don’t include arguments, we get the error message test.py: error: the following arguments are required:
and the missing arguments are listed.
Our help menu gives us some nice hints regarding the difference between arguments and options. As the name suggests, options are optional arguments that can be used to augment or change the function of the script. Option and flags are used interchangeably to describe the same thing. There are short forms starting with -
and long forms starting with --
; both do the same thing, but short forms are abbreviated versions of long forms and can help make commands shorter.
How about we add some arguments to change the function of our script:
import argparse
def main():
parser = argparse.ArgumentParser()
parser.add_argument('say', help='outputs the given string')
parser.add_argument('boom', help='prints the string in uppercase')
parser.add_argument('-sho', '--shout', help='yells both inputs', action='store_true')
parser.add_argument('-shu', '--shush', help='quiets both inputs', action='store_true')
args = parser.parse_args()
if args.shout & args.shush:
print('Cancelled out')
print(args.say)
print(args.boom.upper())
elif args.shout:
print(args.say.upper())
print(args.boom.upper())
elif args.shush:
print(args.say)
print(args.boom)
else:
print(args.say)
print(args.boom.upper())
if __name__ == '__main__':
main()
$ python test.py boo yah -sho
BOO YAH
$ python test.py uh oh -shu
uh oh
$ python test.py take that -shu -sho
Cancelled out take THAT
You might notice that we added action
as a parameter to add_argument
for both of our new flags. Setting action
to "store_true" makes the flag True
if included and False
if not. Most flags include this parameter, as excluding it requires additional input, which is unnecessary if the script is changed depending only on the flag’s inclusion.
In-depth Improvements
The information above is enough for general scripts to be written, but there are some specifics that can enhance our programming further.
Mutually-Exclusive Groups
Let’s start by modifying test.py
. --shout
and --shush
perform opposite functions and, based on how we’ve written the script, cancel each other out. Including both is unintuitive and muddles our script — let’s utilize the add_mutually_exclusive_group()
function included in ArgumentParser()
.
import argparse
def main():
parser = argparse.ArgumentParser()
changer = parser.add_mutually_exclusive_group()
parser.add_argument('say', help='outputs the given string')
parser.add_argument('boom', help='prints the string in uppercase')
changer.add_argument('-sho', '--shout', help='yells both inputs', action='store_true')
changer.add_argument('-shu', '--shush', help='quiets both inputs', action='store_true')
args = parser.parse_args()
if args.shout:
print(args.say.upper())
print(args.boom.upper())
elif args.shush:
print(args.say)
print(args.boom)
else:
print(args.say)
print(args.boom.upper())
if __name__ == '__main__':
main()
$ python test.py -h
usage: test.py [-h] [-sho | -shu] say boom positional arguments: say outputs the given string boom prints the string in uppercase optional arguments: -h, --help show this help message and exit -sho, --shout yells both inputs -shu, --shush quiets both inputs
We see from the help menu under usage that the second flag is [-sho | -shu]
, indicating that you use one flag or the other. If we include both --shout
and --shush
in one command, we get the error message test.py: error: argument -shu/--shush: not allowed with argument -sho/--shout
.
Dealing with integers and choices
For the next demonstration, we’ll move on from test.py
and create a new Python script called numbers.py
. The goal of this script will be to take the sum of 3 integers, then multiply it by some integer indicated by a flag.
import argparse
def main():
parser = argparse.ArgumentParser()
parser.add_argument('x', type=int, help='first value')
parser.add_argument('y', type=int, help='second value')
parser.add_argument('z', type=int, help='third value')
parser.add_argument('-f', '--factor', type=int, help='value by which the sum is multiplied', default=1)
args = parser.parse_args()
print((args.x + args.y + args.z) * args.factor)
if __name__ == '__main__':
main()
$ python numbers.py 1 2 3
6
You might have noticed we added the type
and default
parameters of add_argument
. Without these two, our script would not function as desired:
-
For
argparse
, the default value fortype
is string. Leaving outtype=int
would result in string concatenation instead of addition. Thetype
argument must be changed if you want to deal with anything other than strings. -
If a flag is missing, the default is
None
, even if the flag is an integer. Leaving outdefault=1
here would result in the error messageTypeError: unsupported operand type(s) for *: 'int' and 'NoneType'
If we wanted to adopt a "less is more" philosophy for the script and maximize its multiplication at 3, we could use the choices
parameter as follows:
import argparse
def main():
parser = argparse.ArgumentParser()
parser.add_argument('x', type=int, help='first value')
parser.add_argument('y', type=int, help='second value')
parser.add_argument('z', type=int, help='third value')
parser.add_argument('-f', '--factor', type=int, help='value by which the sum is multiplied',
default=1, choices = [1, 2, 3])
args = parser.parse_args()
print((args.x + args.y + args.z) * args.factor)
if __name__ == '__main__':
main()
$ python numbers.py 1 2 3 -f 2
12
$ python numbers.py 1 2 3 -f 4
usage: numbers.py [-h] [-f {1,2,3}] x y z numbers.py: error: argument -f/--factor: invalid choice: 4 (choose from 1, 2, 3)
As shown above, our options are limited to tripling, doubling, or keeping the sum. Adapting choices
is a very straightforward process that can be very powerful for catching edge cases that might otherwise break your script.