7 Levels of Building Command-Line Interfaces in Python
Execute your Python scripts as bash commands

Writing Python scripts to build command-line interfaces(CLI) is a widely-used technique for DevOps and backend development.
The key to implementing CLI in Python is the built-in argparse module. It provides all necessary functionalities and makes it possible to use a Python script as a bash command.
This article will introduce some essential points of building CLI with Python in 7 levels of complexity.
1. Start a Basic Command-Line Interface with argparse Module
First of all, let’s create a file named test.py and just make a simple argument parser:
import argparse
parser = argparse.ArgumentParser()
parser.parse_args()Since we defined this parser, we can receive arguments from the command line now. Every ArgumentParser instance contains the --help (or -h as a shortcut) option to print useful information.
$ python test.py --help
usage: test.py [-h]
optional arguments:
-h, --help show this help message and exitHowever, because we didn’t define other arguments, the following input will raise an error:
$ python test.py 9
usage: test.py [-h]
test.py: error: unrecognized arguments: 92. Define Positional Arguments
There are two types of arguments in bash commands. One is positional, the other is optional.
For example, the cp command has two positional arguments:
cp source_file dest_fileThe source_file specifies the sources of the file to be copied. And the dest_file argument specifies the location to copy the file.
We can define a positional argument for our Python CLI as well:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("num", type=int)
args = parser.parse_args()
print(args.num**2)As the above code shows, we defined the argument’s name as num and specified its type as int.
Now, as long as the input from terminal is a valid integer, our Python script will print its square:
$ python test.py 9
813. Add More Helpful Information and Description
As with many Linux commands, a good CLI should provide enough information to users through its --help option.
For this purpose, we can specify the dedicated information to the corresponding functions of the argparse module.
import argparse
parser = argparse.ArgumentParser(prog='get_square',description='A CLI to calculate the square of an integer.')
parser.add_argument("num", type=int, help='An integer that needs to get the square value.')
args = parser.parse_args()
print(args.num**2)As demonstrated above, we added the prog and description information into the ArgumentParser instance to give it a name and a detailed description. In addition, we gave the add_argument() function a help message as well to let our users know what this argument is for.
Now, let’s see the output of the -h option again:
$ python test.py -h
usage: get_square [-h] num
A CLI to calculate the square of an integer.
positional arguments:
num An integer that needs to get the square value.
optional arguments:
-h, --help show this help message and exitIt’s more user-friendly and informative, isn’t it? 😊
4. Define Optional Arguments
Besides the positional arguments, optional arguments are also necessary for many command-line interfaces.
The built-in --help option of all ArgumentParser instances is an optional argument. We can add another one to the previous example:
import argparse
parser = argparse.ArgumentParser(prog='get_square',description='A CLI to calculate the square of an integer.')
parser.add_argument("num", type=int, help='An integer that needs to get the square value.')
parser.add_argument('--verbose',help='Print more info.')
args = parser.parse_args()
if args.verbose:
print(f'The square of {args.num} is {args.num**2}')
else:
print(args.num**2)The above program added an optional argument named --verbose. If there is a value for this argument, the program will print more detailed information for the calculation.
Now, let’s try to use it:
$ python test.py 9 --verbose 1
The square of 9 is 81
$ python test.py 9 --verbose 90908
The square of 9 is 81
$ python test.py 9 --verbose
usage: get_square [-h] [--verbose VERBOSE] num
get_square: error: argument --verbose: expected one argumentThe annoying issue is that we have to provide a value for this option to make it work, no matter what value it is. If there is no value at all, as illustrated above, an error message will be raised.
This is because the args.verbose is None if we use it directly without assigning any values to it.
It’s not as convenient as other Linux commands. So we need to optimise it through action parameters.
5. Define Special Actions for Optional Arguments
Let’s change this line of code a bit:
parser.add_argument('--verbose',help='Print more info.', action='store_true')The action parameter of the add_argument() function can give it a guide about how to handle the values of this option.
In our case, the store_true means if the option is specified, the program will assign the value True to args.verbose by default.
And then execute the script with the --verbose option directly:
$ python test.py 9 --verbose
The square of 9 is 81Besides store_true or store_false, Python provides other choices of the action parameters as well. Let’s try a few:
Use the "store_const" action
We can use the store_const parameter as an action and assign a const to the optional argument:
parser.add_argument('--verbose',help='Print more info.', action='store_const', const=1)Therefore, the value of the args.verbose will always be 1.
Use the “count” action
The 'count' parameter counts the number of times an optional argument occurs. Let’s edit our code and try it:
import argparse
parser = argparse.ArgumentParser(prog='get_square',description='A CLI to calculate the square of an integer.')
parser.add_argument("num", type=int, help='An integer that needs to get the square value.')
parser.add_argument('--verbose',help='Print more info.', action='count')
args = parser.parse_args()
if args.verbose==2:
print(f'The square of {args.num} is {args.num**2}')
elif args.verbose==1:
print(f"{args.num}^2 == {args.num**2}")
else:
print(args.num**2)In this time, we can determine how detailed the output is through the number of the --verbose options:
$ python test.py 9 --verbose --verbose
The square of 9 is 81
$ python test.py 9 --verbose
9^2 == 81
$ python test.py 9
81Use the “append” action
The append action stores a list and appends each argument value to a list. In some cases, it will be useful to store multiple values of an optional argument.
To use it, change the following line of the previous Python code:
parser.add_argument('--verbose',help='Print more info.', action='append')Now, we can add multiple values of the --verbose argument:
$ python test.py 9 --verbose 2 --verbose 3 --verbose 5
The verbose values are ['2', '3', '5'].
The square of 9 is 816. Define Shortcuts of Optional Arguments
It’s a bit boring to input the --verbose many times. Like the -h shortcut for the --help option. We can also define a shortcut for our --verbose option as follows:
parser.add_argument('-v','--verbose',help='Print more info.', action='count')After defining the -v shortcut, we can use it directly:
$ python test.py 9 -vv
The square of 9 is 81
$ python test.py 9 -v
9^2 == 817. File Handling in Command Line
The argparse module is also able to handle file-type arguments, which makes it convenient to do some basic file operations through command-line interfaces.
Read a file through command line
If we would like to display the content of a file line by line on the terminal, we may write the Python script as follows:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('f', type=argparse.FileType('r'))
args = parser.parse_args()
for line in args.f:
print(line.strip())Now, let’s execute this Python script on the terminal and pass the test.txt as a file-type argument:
$ python test.py test.txt
Yang is writing a post.
Yang is writing on Medium.As illustrated above, the test.txt file has two lines of sentences. They were printed on the terminal properly with the help of our Python script.
Write data to a file through command line
To write data to a file, we need to change the operator from r to w:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('f', type=argparse.FileType('w'))
args = parser.parse_args()
f = args.f
f.write('Yang is writing')
f.close()The above code will write the sentence “Yang is writing” to the file assigned through a command line interface. The use of it is as follows:
$ python test.py test.txtAfter executing the above command, the “test.txt” file will contain one line of the sentence “Yang is writing.”
Tips: Add a shebang to your Python script
By the way, if you are on Linux or Mac, add a shebang at the beginning of the Python script will save you from typing python every time:
#!/usr/bin/env python3Now, the Python script can be executed on terminal directly:
$ test.py test.txt
Conclusion
Mastering the argparse module in Python will help us design and implement easy-to-use command-line interfaces through Python scripts.
In the cases that we need a customized CLI but don’t bother to write complex bash scripts, this Python module is our best friend. 👍
Thanks for reading. ❤️
Enjoy more:
