How to parse command line arguments in Python
Python is a great language for building command line tools. It's versatile, straight-forward and its tooling and community make it a great candidate for a language to build your command line tools in.
One key feature of command line applications is allowing arguments and options to further refine the execution of the program when calling it. In this post, I'll show you how to build support for command line arguments and options to your Python programs.
Option 1: sys.argv
A good starting point is Python's standard library's sys.argv
. It
has a minimalistic interface to arguments: a single attribute that contains
the name of the script and any arguments passed after that in a list.
Let's say, we have a command line tool for summing up numbers that we call like below:
python sum.py 1 2 3 4 5
sys.argv
will be a list where the first item is
"sum.py"
and the following items are the numbers (as strings):
import sys
print(sys.argv)
# >> ['sum.py', '1', '2', '3', '4', '5']
Hence, for our sum
tool, we can do
import sys
numbers = [int(num) for num in sys.argv[1:]]
print(sum(numbers))
We can even expand our application to be a small calculator by adding the operation keyword before our numbers:
import sys
import math
operation = sys.argv[1]
values = [int(num) for num in sys.argv[2:]]
if operation == 'sum':
print(sum(values))
elif operation == 'multiply':
print(math.prod(values))
else:
print("Unknown operation", file=sys.stderr)
This way, our new calculator.py
would be called by
python calculator.py sum 4 4
While sys.argv
is great and easy-to-use for simple needs, you run
into its limitations quite fast. It requires you to manually cast arguments to
right formats, it makes supporting optional arguments a challenge and all in
all requires a lot of manual checking and boilerplate code.
Let's look at an alternative that helps us build more complex command line
tools, argparse
.
Option 2: argparse
argparse
module
is part of Python's standard library just like sys.argv
. Where
sys.argv
only exposes the command line arguments to the program,
argparse
is a purpose-built tool for describing arguments
declaratively. It packs a lot of base functionality behind a clean interface.
While the module itself has a clean interface, I find its documentation a bit difficult to follow. Every time I end up building command line tools with Python, I have to read through a bunch of blog posts and Stack Overflow posts to get to where I want to go.
Basic syntax
import argparse
parser = argparse.ArgumentParser(description="Calculator")
# [Define arguments here]
args = parser.parse_args()
print(args)
Even without defining any arguments, argparse
gives us one by
default: -h
/ --help
. Try saving above snippet to
calculator.py
and run with
python calculator.py --help
to see what a default help output
looks like.
I've kept the rest of the examples small and contained. When testing them out,
replace # [Define arguments here]
in the above code snippet with
them and try running the script with different arguments.
Declaring arguments
The power of argparse
, like I mentioned earlier, is its
declarative nature. All you have to do is to configure the arguments you want
to accept from the user and the module will take care of parsing, converting,
error handling and building a help page for you.
Arguments are declared with
parser.add_argument
method.
Positional arguments
Let's start by achieving feature parity with our
sys.argv
example: a program that takes arbitrary number of
numbers and calculates the sum of them.
parser.add_argument(
'numbers',
type=int,
nargs='*',
help='Numbers to operate on'
)
Let's break our first add_argument
call down and see what's
happening here.
-
'numbers'
is the name of the argument. All these positional arguments get stored in variableargs.numbers
and in the help page, the user will see its name. -
type=int
does two things: first, it casts the arguments into integers and secondly, it will show an error to the user if the arguments are not integers. -
nargs='*'
defines the number of arguments.*
stands for 0 or more. Other options are?
which means 0 or 1,+
which means 1 or more and a number (for example2
) which means exactly 2. -
help='Numbers to operate on'
tells your user how to use the program and its arguments. Run the program with--help
and you'll see what it looks like.
Named arguments / optional arguments / flags
A loved child has many names. While positional arguments are often called that, the other type of arguments are sometimes referred as named arguments (as we give them names), optional arguments (as they are optional) or flags (for named arguments that are boolean values).
To differentiate these from positional arguments, the name of the argument
needs to begin with one or two dashes. "operation"
is positional,
"--operation"
or "-o"
are optional/named.
An optional argument
In the previous example, we defined positional arguments that are by default
mandatory unless we use *
or ?
value for
nargs
that allow the number of arguments to be 0.
Optional arguments lets us define arguments that are – as the name suggests – optional.
parser.add_argument(
'--base',
default=10,
type=int,
help="Number base (for example, 2 for binary, 10 for decimal)"
)
Here we add an optional argument for doing the calculations in given number base.
-
default=10
sets up a default that is only deviated from if the user provides--base [N]
argument.
Named, required argument
Another use case for defining a non-positional argument is to give them a name and to add support for multiple different arguments without relying on their positions.
parser.add_argument(
'--operation',
required=True,
help="Operation you want to run on the numbers"
)
Named arguments can be provided in any order so the following result in the same end result
python calculator.py 1 2 3 --operation add
python calculator.py --operation add 1 2 3
Supported values
In our previous example, the user could write anything as the operation which is not great UX since we'd only ever support a limited amount of them.
To guide our user to define a specific, supported operation, we can provide
choices
argument which is a list of accepted values.
parser.add_argument(
'--operation',
choices=['add', 'multiply', 'substract', 'divide'],
default='add',
help="Operation you want to run on the numbers"
)
A boolean flag
Sometimes we only care about the existence of an argument and don't need an associated value. That's called a boolean flag.
parser.add_argument(
'--absolute',
action="store_true",
help="Return absolute value of the result"
)
-
action
argument allows us to customize what we want to do with the value.store_true
andstore_false
tells our program to store the correspondingTrue
orFalse
value respectively. -
args.absolute
will beTrue
if--absolute
flag was passed andFalse
otherwise.
Multiple names
Often named arguments have two forms: long form like --help
and
short form like -h
. You can define multiple names by providing
add_argument
multiple positional arguments:
parser.add_argument(
'--absolute',
'-a',
action="store_true"
)
The value will be stored to the first defined long-form name, in this case
args.absolute
. You can change this using
dest
parameter to add_argument
:
parser.add_argument(
'--absolute',
'-a',
action="store_true",
dest="is_absolute"
)
With this, you can access the value from args.is_absolute
.
Learn more
- Command Line Interface Guidelines: An open-source guide to help you write better command-line programs, taking traditional UNIX principles and updating them for the modern day.
- argparse documentation
- Why I love using command line interface?: A blog post about command line interface's greatness