Creating CLI Commands using Argparse in Python
Tags: python, programming, cli
Published: 2024-03-31 07:21:07

Python has a rich and versatile collection of standard libraries, making it one of the better programming languages, so it needs to cover all the areas of programming. 

If developers want to write the scripts for the command line using Python, then there must be some libraries that allow them to write command line scripts conveniently. Python has a library called `argparse` that helps to create exemplary command-line interfaces for the command-line scripts.

In this article, we'll be going to learn:

- **What is `argparse` in Python**

- **How to create a basic CLI in Python using `argparse`**

- **What are the advanced features of Python `argparse`**

## What is CLI

A command line interface, short for **CLI**, is a medium or type of interface that allows us to interact with a command line script. Python has many libraries that allow us to write the command line interface for our scripts. We'll use a library called `argparse` to create a CLI in Python.

It is a standard way to create a CLI in Python, and `argparse` came as a replacement for the modules like [optparse](https://docs.python.org/3/library/optparse.html) and [getopt](https://docs.python.org/3/library/getopt.html) because they lacked some significant features.

## Getting to know CLI

Before we proceed any further, we need to know how the command line interface works, so open up your terminal on your computer and execute a command ***'python'*** to start using Python commands in your command line.

```shell
>python
....
Python 3.10.5 (tags/v3.10.5:f377153, Jun  6 2022, 16:14:13) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> print("Hello Geeks!")
Hello Geeks!
>>> 3 % 6
3
>>> sum([4, 19, 111])
134
```
Here we can execute the Python commands directly in our CLI. Fortunately, we don't need any Python interpreter to get started. Still, we can add an **option**(type of argument) with the argument to get the extra information.

```shell
>python --version
....
Python 3.10.5

>python -q
....
>>> print("Python has started without prompting copyright and version messages.")
Python has started without prompting copyright and version messages.
```
Here we obtained the version of Python that we are currently using by just adding the `--version` option to the `python` argument, and in the second command, we used the `-q` option to start the Python interpreter without prompting the version and copyright messages that it usually shows on startup.

To understand arguments, options, and parameters, consider the following example:

```shell
>dir /d /r D:\SACHIN\Pycharm\cli_flask
```
- **dir:** The name of the command we are executing

- **/d:** An option to display all directories in the current path

- **/r:** An option to display the read-only files

- **D:\SACHIN\Pycharm\cli_flask:** A parameter to list the directories in the specified path

To explain precisely,

An **argument** is a command that we want to execute.

An **option** is a part of an argument or type of argument used to modify the behavior of the command line.

A **parameter** is a type of argument that provides additional information to the command.

## Creating a basic command using `argparse`

We can create an essential command for CLI in a few steps using `argparse`. Let's understand the measures included in the process of making the command.

- **Importing the `argparse` because it's a Python library**

- **Creating the parser**

- **Adding the positional and optional arguments to the parser**

- **Executing the parser using the `parse_args()`**

Let's assume we have a file named `command.py` in which we write the basic command to get the **factorial** of the integer specified in the **command line interface** or **CLI**.

```python
# Importing argparse library
import argparse

from math import factorial

# Creating the parser
parser = argparse.ArgumentParser(description='Get the factorial of the integer.')

# Adding the argument
parser.add_argument('Factorial',
                    metavar='integer',
                    type=int,
                    help='factorial of the integer')

# Executing the parser using parse_args() method
args = parser.parse_args()

fact = args.Factorial
print(factorial(fact))
```
We've coded our first-ever command for CLI and now let's understand our code. We can divide our code into four parts.

First, we imported the `argparse` library and then created the ***parser*** using the `ArgumentParser()` method in which we passed the `detail` argument to specify the detail of our command. 

Next, we added the argument to the **parser** using the `add_argument` method, which holds the necessary information passed into the arguments inside the method. Finally, we executed our **parser** using the `parse_args()` method.

If we run our command in the CLI without specifying any value, then we will get an **error** and a **usage message**.

```shell
>python command.py  
usage: command.py [-h] integer
command.py: error: the following arguments are required: integer
```
The program detected that we needed a **positional argument** (`integer`) to complete the execution, but the execution was interrupted due to the absence of the positional argument.

We can see that our command accepts an **optional** `-h` flag.

```shell
>python command.py -h
usage: command.py [-h] integer

Get the factorial of the integer.

positional arguments:
  integer     factorial of the integer

options:
  -h, --help  show this help message and exit
```
If we execute our command with the **positional** argument it needs, we'll get the desired result.

```shell
>python command.py 6 
720
```

## Advanced usage of `argparse`

We learned the basic usage of the `argparse` library, and now we can create an essential command for CLI. However, this is not it, we can do many more things using this library. We'll see some advanced usage of the `argparse` library.

### Display custom help message

The `argparse` library generates usage help if not provided, but we can **customize the help message using the `usage` keyword**.

```python
parser = argparse.ArgumentParser(
    usage='command.py [options] integer',
    description='Get the factorial of the integer.')
```
Now we can see the different **usage messages** if we execute the program.

```shell
>python command.py
usage: factorial [options] integer
factorial: error: the following arguments are required: integer
```
### Setting the program name

In the previous section, we saw the **Python script name** when we executed the program in the CLI because the library uses the `sys.argv[0]` to set the **program's name**. However, we can specify the program name using the <code>**prog**</code> keyword.

```python
parser = argparse.ArgumentParser(
    prog='factorial',
    description='Get the factorial of the integer.')
```
We will now see our specified name, `factorial`, of the program in the CLI instead of the Python script name.

```shell
>python command.py
usage: factorial [-h] integer
factorial: error: the following arguments are required: integer
```
### Displaying text before and after the arguments

We can **customize** the text we want to display **before** and **after** in the **help** text by using the following keywords.

- **description**: for the text shown before the help text

- **epilog**: for the text shown after the help text

We have already seen the use of <code>**description**</code>. This time we'll use the <code>**epilog**</code> to display the text after the **help** text.

```python
parser = argparse.ArgumentParser(
    description='Get the factorial of the integer.',
    epilog='Have you understood the process? ')
```
We will see the customized message after the **help** text.

```shell
>python command.py -h
usage: command.py [-h] integer

Get the factorial of the integer.

positional arguments:
  integer     factorial of the integer

options:
  -h, --help  show this help message and exit

Have you understood the process?
```
### Customizing prefix character

The **optional arguments** in the CLI command are generally prefixed with (`-`), a standard prefix char. The `argparse` library has a feature that allows us to customize the **prefix chars**.

The <code>**prefix_chars**</code> keyword lets us customize the prefix character while defining the parser.

```python
parser = argparse.ArgumentParser(
    description='Get the factorial of the integer.',
    prefix_chars='$')
```
Now, in the output, we won't see (`-`) as the prefix char for the optional argument instead, we will see the (`$`) as the prefix char.

```shell
>python command.py   
usage: command.py [$h] integer
command.py: error: the following arguments are required: integer
```
Now our program does not support the `-h` flag but the `$h` flag. The help text has also changed accordingly.

### Enable and disable help

Python `argparse` library automatically generates the help text without having to code anything. However, sometimes we wanted to disable the feature. We can add the `add_help` keyword when creating the parser.

```python
parser = argparse.ArgumentParser(
    description='Get the factorial of the integer.',
    add_help=False)
```
We've added the `add_help` keyword in the above code and set its value to `False`. It ensures that the program does not accept the `-h` flag anymore.

```shell
>python command.py
usage: command.py integer
command.py: error: the following arguments are required: integer
```

### Allowing and disallowing abbreviation

One of the features the Python `argparse` library provides is the ability to handle abbreviations. Consider the following example that prints the square of the specified integer.

```python
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--square', action='store', type=int)
parser.add_argument('--sum', action='store', type=int)

args = parser.parse_args()
print(args.square**2)
```
We can shorten the optional argument until the abbreviation leads to the wrong interpretation.

```shell
>python abbreviation.py --square 4 
16

>python abbreviation.py --squar 4  
16

>python abbreviation.py --squa 4  
16

>python abbreviation.py --squ 4  
16

>python abbreviation.py --sq 4  
16
```
We can shorten the optional argument until `--sq 4`. What happens if we execute the program specifying `--s 4`? It might lead to an error because `argparse` doesn't know whether we want to specify **4** to the `--square` or the `--sum` argument.

```shell
>python abbreviation.py --s 4   
usage: abbreviation.py [-h] [--square SQUARE] [--sum SUM]
abbreviation.py: error: ambiguous option: --s could match --square, --sum
```
However, we can force users to specify the full name of the option by disabling the feature by adding the `allow_abbrev` while creating the parser.

```python
import argparse

parser = argparse.ArgumentParser(allow_abbrev=False)
parser.add_argument('--square', action='store', type=int)
parser.add_argument('--sum', action='store', type=int)

args = parser.parse_args()

print(args.square**2)
```
Now, if we try to execute the program using an abbreviation, the program will throw an error.

```shell
>python abbreviation.py --squa 4  
usage: abbreviation.py [-h] [--square SQUARE]
abbreviation.py: error: unrecognized arguments: --squa 4
```
### Setting the name or flag of the arguments

There are two types of arguments that we can use in our command line interface:

**Positional arguments**

In the previous examples, we've already seen the positional arguments. In the example above, `Factorial` was the **positional argument**, and our program couldn't work without it.

**Positional arguments** are the types of argument that we use in command to perform some operation. They are called **positional** arguments because their position defines their function.

```python
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('Square', type=int, help='square of the integer')

args = parser.parse_args()

print(args.Square**2)
```
Here, `Square` is the positional argument, and when we specify an integer, the program returns the square of that integer. By default, the positional argument is treated as a **String**, so we used the `type` keyword and set its value to `int` to typecast the **string into the integer**.

**Optional arguments**

The optional arguments are used to perform the additional tasks, and when they are used, they can modify the command's behavior at runtime.

For example, if we look at the `ls` command that lists the files in a current directory and if we use the `-l` argument, which is an optional argument, it modifies the output and returns the extra information.

```shell
$ ls -l
```
Optional arguments are not mandatory, and we won't get any errors if we don't specify them. They are generally prefixed with a (`-`) dash or (`--`) double dash.

```python
import argparse
import math

parser = argparse.ArgumentParser()
parser.add_argument('-s', 
                    '--sq_root', 
                    type=int, 
                    help='square root of the integer')

args = parser.parse_args()

sqrt = args.sq_root

print(math.sqrt(sqrt))
```
**Output**

```shell
>python op_arg.py --sq_root 9 
3.0

>python op_arg.py -s 9        
3.0
```
### Setting the action

When we add an argument to the command line interface, we can also define the kind of action to take when the argument is specified.

The `action` keyword argument specifies how the command-line arguments should be handled. Many actions are available and ready to use:

- **store**: stores the argument's value. This is the default action.

- **store_const**: stores a constant value when the optional arguments are specified.

- **store_true** and **store_false**: used for storing the **boolean** values `True` and `False` respectively when the optional arguments are specified and stores a **False** and **True** elsewhere, respectively.

- **append**: stores a list and appends each argument value to the list.

- **append_const**: stores a list and appends the constant value to the list each time the option is provided.

- **count**: counts the number of times the option is provided.  

- **help**: shows the help message and then exits.

- **version**: shows the program's version information and exits.

- **extend**: stores a list and extends each argument value to the list. It was added in Python v3.8.

Let's understand what these actions can do with simple examples.

We've used the <code>**action='store'**</code> in the following example, which will store the specified value.

```python
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--store', action='store')

args = parser.parse_args()
print(args.store)
print(vars(args))
```
**Output**

```shell
>python action.py --store 23 
23
{'store': '23'}

>python action.py --store hello
hello
{'store': 'hello'}
```
The <code>**action='store_const'**</code> stores the value specified by the [const](https://docs.python.org/3/library/argparse.html#const) keyword. We've defined the `const` keyword and set its value to `hello`.

```python
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--sc', action='store_const', const='hello')

args = parser.parse_args()
print(vars(args))
```
We just provided the `--sc` argument, and we got the value specified in the `const` keyword because the value of `args.sc` is now `hello`.

```shell
>python action.py --sc     
{'sc': 'hello'}
```
The <code>**action='store_true'**</code> stores the boolean `True` and stores the `False` elsewhere when the arguments are passed. We can use <code>**action='store_false'**</code> for the opposite behavior.

```python
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--st', action='store_true')

args = parser.parse_args()
print(vars(args))
```
**Output**

```shell
>python action.py        
{'st': False}

>python action.py --st
{'st': True}
```
The <code>**action='append'**</code> creates a list of values passed in the CLI.

```python
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--append', action='append')

args = parser.parse_args()
print(vars(args))
```
**Output**

```shell
>python action.py --append hello --append world
{'append': ['hello', 'world']}
```
The <code>**action='append_const'**</code> is the same as `append`, but it appends the same constant value.

```python
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--ac', action='append_const', const='2')

args = parser.parse_args()
print(vars(args))
```
**Output**

```shell
>python action.py --ac --ac --ac               
{'ac': ['2', '2', '2']}
```
The <code>**action='count'**</code> counts the number of times the argument is passed.

```python
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--count', action='count', default=0)

args = parser.parse_args()
print(vars(args))
```
**Output**

```shell
>python action.py --count --count --count --count
{'count': 4}
```
We've already seen the help message in our examples previously, which is enabled for the `-h` flag. The <code>**action='help'**</code> lets us use another flag for the help message.

```python
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-x', action='help')

args = parser.parse_args()
print(vars(args))
```
**Output**

```shell
>python action.py -x
usage: action.py [-h] [-x]

options:
  -h, --help  show this help message and exit
  -x
```
The <code>**action='version'**</code> helps us to get the program's version, but it expects a `version` keyword.

```python
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-z', action='version', version='1.11')

args = parser.parse_args()
print(vars(args))
```
**Output**

```shell
>python action.py -z
1.11
```
The <code>**action='extend'**</code> extends the argument values to the list.

```python
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-e', action='extend', nargs='+')

args = parser.parse_args()
print(vars(args))
```
**Output**

```shell
>python action.py -e 2 4 6 3 -e 0 9
{'e': ['2', '4', '6', '3', '0', '9']}
```

### Setting the default value

Since the optional arguments are not mandatory, they can be omitted by the user in the command line interface. In this situation, the value is generally set to `None`.

We can set the default value for an argument when it is not provided using the `default` keyword.

```python
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-d', default=5)

args = parser.parse_args()

print(vars(args))
```
We'll get the following output if we execute the program without specifying the `-d` option.

```shell
>python default.py     
{'d': 5}
```
We didn't receive any error because option `-d` is set to **5**.

### Setting the type of the argument

By default, the input arguments are treated as a string, and we can typecast the input arguments in the desired datatype using the `type` keyword. We've already seen examples where we used the `type` keyword.

```python
import argparse
import math

parser = argparse.ArgumentParser()
parser.add_argument('-f', type=int,)

args = parser.parse_args()

print(math.factorial(args.f))
```
`type=int` will ensure that the argument's value must be an integer instead of a string.

```shell
>python type.py -f 2
2
```
The value of the argument is checked at the runtime. If there is any datatype other than the **integer** provided in the command line, then the program will throw an error.

```shell
>python type.py -f a 
usage: type.py [-h] [-f F]
type.py: error: argument -f: invalid int value: 'a'
```
The error clearly states that the value `a` is an invalid integer, and we need to pass an **integer** instead of a **string**.

### Making the argument to be required

The user can omit the optional arguments. However, we can force the user to specify the value for an optional argument by making the argument to be required by using the <code>**required**</code> keyword.

```python
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-d', required=True)

args = parser.parse_args()

print(vars(args))
```
We used the `required` keyword and set its value to `True`, which forces the user to specify the value for the argument.

```shell
>python required.py
usage: required.py [-h] -d D
required.py: error: the following arguments are required: -d
```
**Note: It is a bad practice to make an optional argument to be required by the user because the name itself depicts that it is optional, and the user wouldn't expect to set a value for the argument.**

### Setting the number of values the argument can take

Usually, we specify the single value to the argument, which is the default behavior. We can change this default behavior by specifying the number of values an argument can consume using the `nargs` keyword.

```python
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--value', type=int, nargs=2)

args = parser.parse_args()

print(args.value)
```
The program will accept only two values, not more or less than that.

```shell
>python nargs.py --value 1
usage: nargs.py [-h] [--value VALUE VALUE]
nargs.py: error: argument --value: expected 2 arguments

>python nargs.py --value 1 2
[1, 2]

>python nargs.py --value 1 2 34
usage: nargs.py [-h] [--value VALUE VALUE]
nargs.py: error: unrecognized arguments: 34
```
As we can see, when we provided a **single** and **more than two values**, the program threw an **error**, but when we provided the **two** values, the program **returned the list containing the two values**.

Instead of using any integer, the `nargs` keyword also accepts the following:

- **?**: a single value will be consumed, which can be optional

- *: an extensible number of values, which will be gathered into a list

- **+**: much like *, but it requires at least one value

- **argparse.REMAINDER**: all the values that are remaining in the command line

In the following example, the **positional argument** `value` accepts a single value. If the value is not provided, it returns the value set in the `default` keyword.

```python
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('value',
                    nargs='?',
                    default='This is a default value.')

args = parser.parse_args()

print(args.value)
```
We can now set the specific value for the `value` argument. If the value is not provided, it will prompt the default value in the command line.

```shell
>python nargs.py 2
2

>python nargs.py          
This is a default value.
```
If we wanted to provide a flexible number of values and gather them into a list, then we need to use the ***** value for the `nargs` keyword.

```python
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('value',
                    nargs='*',
                    default='This is a default value.')

args = parser.parse_args()

print(args.value)
```
The above code allows us to set a flexible number of values, which we can see in the following output.

```shell
>python nargs.py more than one value
['more', 'than', 'one', 'value']

>python nargs.py                    
This is a default value.
```
We can use the **+** value for the `nargs` keyword when we want a variable number of values, but we need to ensure that at least one value is specified.

```python
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('value',
                    nargs='+',)

args = parser.parse_args()

print(args.value)
```
The above code can accept a variable number of values but make sure at least one value is specified. Otherwise, it will throw an error.

```shell
>python nargs.py needs at least one value
['needs', 'at', 'least', 'one', 'value']

>python nargs.py                         
usage: nargs.py [-h] value [value ...]
nargs.py: error: the following arguments are required: value
```
As we can see, when we executed the program without any **value**, it threw an error, while when we provided multiple values, it returned the list containing the values.

Consider the following example, where we're using the `argparse.REMAINDER` value to the `nargs` for the `remaining` argument to grab all the remaining values that have been specified in the command line and put them in a list.

```python
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('value')
parser.add_argument('remaining', nargs=argparse.REMAINDER)

args = parser.parse_args()

print(f'First value: {args.value}')
print(f'Other values: {args.remaining}')
```
If we execute the program, the **first value** will be assigned to the **first argument**, and the **remaining** will be assigned to the **second argument**.

```shell
>python nargs.py hey there geeks
First value: hey
Other values: ['there', 'geeks']
```

### Displaying an alternate name for the argument

Using the `metavar` keyword, we can set the name for our argument that will show in the usage message.

```python
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-s',
                    '--square',
                    metavar='Square',)

args = parser.parse_args()

print(vars(args))
```
If we execute the program with the `-h` flag, we can see the name **Square** will be assigned to the `-s` and `--square` options in the help text.

```shell
>python metavar.py -h
usage: metavar.py [-h] [-s Square]

options:
  -h, --help            show this help message and exit
  -s Square, --square Square
```

## Conclusion

This tutorial is a bit lengthy, but it is meant to be because it has detailed information about the `argparse` library in Python, allowing us to build the command-line interface.

We've covered the basic and advanced usage of the `argparse` with simple examples. We are now able to build our command-line interface conveniently.

Let's review what we've learned:

- what is Python `argparse` library and how to use it

- created the basic command-line interface

- what are the advanced usage of the Python `argparse` library

---
🏆**Other articles you might be interested in if you liked this one**

✅[Split your string into an array of words using the split() method in Python](https://geekpython.in/python-split-method).

✅[Map a function to each item in an iterable using the map() function](https://geekpython.in/map-function-in-python).

✅[Serialize and deserialize Python objects using the pickle module](https://geekpython.in/pickle-module-in-python).

✅[Why if \_\_name\_\_ == ‘\_\_main\_\_’ is used in Python programs](https://geekpython.in/understanding-if-__name__-__main__-in-python-programs)?

✅[Create a WebSocket server and client in Python](https://geekpython.in/build-websocket-server-and-client-using-python).

✅[Upload and display images on the frontend using Flask](https://geekpython.in/render-images-from-flask).

---

**That's all for now**

**Keep Coding✌✌**