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 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
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,
Option 2: argparse
argparse module is part of Python's standard library just like
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.
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:
--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.
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
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 variable
args.numbersand in the help page, the user will see its name.
type=intdoes 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 example
2) which means exactly 2.
help='Numbers to operate on'tells your user how to use the program and its arguments. Run the program with
--helpand 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,
"-o" are optional/named.
An optional argument
In the previous example, we defined positional arguments that are by default mandatory unless we use
? 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=10sets up a default that is only deviated from if the user provides
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
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" )
actionargument allows us to customize what we want to do with the value.
store_falsetells our program to store the corresponding
--absoluteflag was passed and
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
parser.add_argument( '--absolute', '-a', action="store_true", dest="is_absolute" )
With this, you can access the value from
- 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
Sign up for Syntax Error, a monthly newsletter that helps developers turn a stressful debugging situation into a joyful exploration.
If you found this post interesting, consider sharing it with your community or following me on Mastodon.