Juha-Matti Santala
Community Builder. Dreamer. Adventurer.

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 variable args.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 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 --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 and store_false tells our program to store the corresponding True or False value respectively.
  • args.absolute will be True if --absolute flag was passed and False 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