Monday, March 20, 2023
HomePythonConstruct Command-Line Interfaces With Python's argparse – Actual Python

Construct Command-Line Interfaces With Python’s argparse – Actual Python


Command-line apps will not be widespread within the basic consumer’s area, however they’re current in growth, knowledge science, methods administration, and lots of different operations. Each command-line app wants a user-friendly command-line interface (CLI) to be able to work together with the app itself. In Python, you’ll be able to create full-featured CLIs with the argparse module from the customary library.

On this article, you’ll discover ways to:

  • Get began with command-line interfaces
  • Manage and lay out a command-line app mission in Python
  • Create command-line interfaces with Python’s argparse
  • Deeply customise your CLIs with some highly effective options of argparse

To get essentially the most out of this tutorial, you ought to be accustomed to Python programming, together with ideas similar to object-oriented programming, script growth and execution, and Python packages and modules. It’ll even be useful if you happen to’re accustomed to basic ideas and matters associated to utilizing a command line or terminal.

Attending to Know Command-Line Interfaces

For the reason that invention of computer systems, people have all the time wanted and located methods to work together and share data with these machines. The data alternate has flowed amongst people, pc software program, and {hardware} elements. The shared boundary between any two of those components is generically referred to as an interface.

In software program growth, an interface is a particular a part of a given piece of software program that enables interplay between elements of a pc system. In terms of human and software program interplay, this very important part is named the consumer interface.

You’ll discover several types of consumer interfaces in programming. Most likely, graphical consumer interfaces (GUIs) are the commonest at this time. Nonetheless, you’ll additionally discover apps and packages that present command-line interfaces (CLIs) for his or her customers. On this tutorial, you’ll study CLIs and easy methods to create them in Python.

Command-Line Interfaces (CLIs)

Command-line interfaces will let you work together with an utility or program by your working system command line, terminal, or console.

To know command-line interfaces and the way they work, contemplate this sensible instance. Say that you’ve a listing referred to as pattern containing three pattern recordsdata. If you happen to’re on a Unix-like working system, similar to Linux or macOS, go forward and open a command-line window or terminal within the mother or father listing after which execute the next command:

$ ls pattern/
hey.txt     lorem.md      realpython.md

The ls Unix command lists the recordsdata and subdirectories contained in a goal listing, which defaults to the present working listing. The above command name doesn’t show a lot details about the content material of pattern. It solely shows the filenames on the display.

Suppose you need richer details about your listing and its content material. In that case, you don’t want to go searching for a program aside from ls as a result of this command has a full-featured command-line interface with a helpful set of choices that you should utilize to customise the command’s habits.

For instance, go forward and execute ls with the -l choice:

$ ls -l pattern/
complete 24
-rw-r--r--@ 1 consumer  workers    83 Aug 17 22:15 hey.txt
-rw-r--r--@ 1 consumer  workers  2609 Aug 17 22:15 lorem.md
-rw-r--r--@ 1 consumer  workers   428 Aug 17 22:15 realpython.md

The output of ls is kind of totally different now. The command shows far more details about the recordsdata in pattern, together with permissions, proprietor, group, date, and dimension. It additionally reveals the full area that these recordsdata use in your pc’s disk.

This richer output outcomes from utilizing the -l choice, which is a part of the Unix ls command-line interface and permits the detailed output format.

Instructions, Arguments, Choices, Parameters, and Subcommands

All through this tutorial, you’ll study instructions and subcommands. You’ll additionally study command-line arguments, choices, and parameters, so you need to incorporate these phrases into your tech vocabulary:

  • Command: A program or routine that runs on the command line or terminal window. You’ll sometimes determine a command with the identify of the underlying program or routine.

  • Argument: A required or non-obligatory piece of data {that a} command makes use of to carry out its meant motion. Instructions sometimes settle for one or many arguments, which you’ll be able to present as a whitespace-separated or comma-separated listing in your command line.

  • Possibility, often known as flag or swap: An non-obligatory argument that modifies a command’s habits. Choices are handed to instructions utilizing a particular identify, like -l within the earlier instance.

  • Parameter: An argument that an choice makes use of to carry out its meant operation or motion.

  • Subcommand: A predefined identify that may be handed to an utility to run a particular motion.

Think about the pattern command assemble from the earlier part:

On this instance, you’ve mixed the next elements of a CLI:

  • ls: The command’s identify or the app’s identify
  • -l: An choice, swap, or flag that allows detailed outputs
  • pattern: An argument that gives extra data to the command’s execution

Now contemplate the next command assemble, which showcases the CLI of Python’s package deal supervisor, referred to as pip:

$ pip set up -r necessities.txt

This can be a widespread pip command assemble, which you’ve in all probability seen earlier than. It means that you can set up the necessities of a given Python mission utilizing a necessities.txt file. On this instance, you’re utilizing the next CLI elements:

  • pip: The command’s identify
  • set up: The identify of a subcommand of pip
  • -r: An choice of the set up subcommand
  • necessities.txt: An argument, particularly a parameter of the -r choice

Now you already know what command-line interfaces are and what their essential elements or elements are. It’s time to discover ways to create your individual CLIs in Python.

Getting Began With CLIs in Python: sys.argv vs argparse

Python comes with a few instruments that you should utilize to put in writing command-line interfaces to your packages and apps. If you could rapidly create a minimal CLI for a small program, then you should utilize the argv attribute from the sys module. This attribute routinely shops the arguments that you just cross to a given program on the command line.

Utilizing sys.argv to Construct a Minimal CLI

For instance of utilizing argv to create a minimal CLI, say that you could write a small program that lists all of the recordsdata in a given listing, just like what ls does. On this state of affairs, you’ll be able to write one thing like this:

# ls_argv.py

import sys
from pathlib import Path

if (args_count := len(sys.argv)) > 2:
    print(f"One argument anticipated, obtained {args_count - 1}")
    increase SystemExit(2)
elif args_count < 2:
    print("You should specify the goal listing")
    increase SystemExit(2)

target_dir = Path(sys.argv[1])

if not target_dir.is_dir():
    print("The goal listing does not exist")
    increase SystemExit(1)

for entry in target_dir.iterdir():
    print(entry.identify)

This program implements a minimal CLI by manually processing the arguments offered on the command line, that are routinely saved in sys.argv. The primary merchandise in sys.argv is all the time this system’s identify. The second merchandise would be the goal listing. The app shouldn’t settle for multiple goal listing, so the args_count should not exceed 2.

After checking the content material of sys.argv, you create a pathlib.Path object to retailer the trail to your goal listing. If this listing doesn’t exist, then you definitely inform the consumer and exit the app. The for loop lists the listing content material, one entry per line.

If you happen to run the script out of your command line, then you definitely’ll get the next outcomes:

$ python ls_argv.py pattern/
hey.txt
lorem.md
realpython.md

$ python ls_argv.py
You should specify the goal listing

$ python ls_argv.py pattern/ other_dir/
One argument anticipated, obtained 2

$ python ls_argv.py non_existing/
The goal listing does not exist

Your program takes a listing as an argument and lists its content material. If you happen to run the command with out arguments, then you definitely get an error message. If you happen to run the command with multiple goal listing, you additionally get an error. Operating the command with a nonexistent listing produces one other error message.

Despite the fact that your program works okay, parsing command-line arguments manually utilizing the sys.argv attribute isn’t a scalable answer for extra complicated CLI apps. In case your app must take many extra arguments and choices, then parsing sys.argv will likely be a posh and error-prone process. You want one thing higher, and also you get it in Python’s argparse module.

Making a CLI With argparse

A way more handy strategy to create CLI apps in Python is utilizing the argparse module, which comes within the customary library. This module was first launched in Python 3.2 with PEP 389 and is a fast strategy to create CLI apps in Python with out putting in a third-party library, similar to Typer or Click on.

This module was launched as a alternative for the older getopt and optparse modules as a result of they lacked some vital options.

Python’s argparse module means that you can:

  • Parse command-line arguments and choices
  • Take a variable variety of parameters in a single choice
  • Present subcommands in your CLIs

These options flip argparse into a robust CLI framework which you could confidently depend on when creating your CLI functions. To make use of Python’s argparse, you’ll have to observe 4 simple steps:

  1. Import argparse.
  2. Create an argument parser by instantiating ArgumentParser.
  3. Add arguments and choices to the parser utilizing the .add_argument() methodology.
  4. Name .parse_args() on the parser to get the Namespace of arguments.

For instance, you should utilize argparse to enhance your ls_argv.py script. Go forward and create ls.py with the next code:

# ls.py v1

import argparse
from pathlib import Path

parser = argparse.ArgumentParser()

parser.add_argument("path")

args = parser.parse_args()

target_dir = Path(args.path)

if not target_dir.exists():
    print("The goal listing does not exist")
    increase SystemExit(1)

for entry in target_dir.iterdir():
    print(entry.identify)

Your code has modified considerably with the introduction of argparse. Essentially the most notable distinction from the earlier model is that the conditional statements to test the arguments offered by the consumer are gone. That’s as a result of argparse routinely checks the presence of arguments for you.

On this new implementation, you first import argparse and create an argument parser. To create the parser, you utilize the ArgumentParser class. Subsequent, you outline an argument referred to as path to get the consumer’s goal listing.

The following step is to name .parse_args() to parse the enter arguments and get a Namespace object that accommodates all of the consumer’s arguments. Observe that now the args variable holds a Namespace object, which has a property for every argument that’s been gathered from the command line.

On this instance, you solely have one argument, referred to as path. The Namespace object means that you can entry path utilizing the dot notation on args. The remainder of your code stays the identical as within the first implementation.

Now go forward and run this new script out of your command line:

$ python ls.py pattern/
lorem.md
realpython.md
hey.txt

$ python ls.py
utilization: ls.py [-h] path
ls.py: error: the next arguments are required: path

$ python ls.py pattern/ other_dir/
utilization: ls.py [-h] path
ls.py: error: unrecognized arguments: other_dir/

$ python ls.py non_existing/
The goal listing does not exist

The primary command prints the identical output as your unique script, ls_argv.py. In distinction, the second command shows output that’s fairly totally different from in ls_argv.py. This system now reveals a utilization message and points an error telling you that it’s essential to present the path argument.

Within the third command, you cross two goal directories, however the app isn’t ready for that. Subsequently, it reveals the utilization message once more and throws an error letting you already know concerning the underlying drawback.

Lastly, if you happen to run the script with a nonexistent listing as an argument, then you definitely get an error telling you that the goal listing doesn’t exist, so this system can’t do its work.

A brand new implicit characteristic is now out there to you. Now your program accepts an non-obligatory -h flag. Go forward and provides it a attempt:

$ python ls.py -h
utilization: ls.py [-h] path

positional arguments:
  path

choices:
  -h, --help  present this assist message and exit

Nice, now your program routinely responds to the -h or --help flag, displaying a assist message with utilization directions for you. That’s a very neat characteristic, and also you get it at no cost by introducing argparse into your code!

With this fast introduction to creating CLI apps in Python, you’re now able to dive deeper into the argparse module and all its cool options.

Creating Command-Line Interfaces With Python’s argparse

You should utilize the argparse module to put in writing user-friendly command-line interfaces to your functions and initiatives. This module means that you can outline the arguments and choices that your app would require. Then argparse will care for parsing these arguments and choices of sys.argv for you.

One other cool characteristic of argparse is that it routinely generates utilization and assist messages to your CLI apps. The module additionally points errors in response to invalid arguments and extra.

Earlier than diving deeper into argparse, you could know that the module’s documentation acknowledges two several types of command-line arguments:

  1. Positional arguments, which you already know as arguments
  2. Elective arguments, which you already know as choices, flags, or switches

Within the ls.py instance, path is a positional argument. Such an argument known as positional as a result of its relative place within the command assemble defines its function.

Elective arguments aren’t necessary. They will let you modify the habits of the command. Within the ls Unix command instance, the -l flag is an non-obligatory argument, which makes the command show an in depth output.

With these ideas clear, you’ll be able to kick issues off and begin constructing your individual CLI apps with Python and argparse.

Making a Command-Line Argument Parser

The command-line argument parser is an important a part of any argparse CLI. All of the arguments and choices that you just present on the command line will cross by this parser, which is able to do the onerous give you the results you want.

To create a command-line argument parser with argparse, you could instantiate the ArgumentParser class:

>>>

>>> from argparse import ArgumentParser

>>> parser = ArgumentParser()
>>> parser
ArgumentParser(
    prog='',
    utilization=None,
    description=None,
    formatter_class=<class 'argparse.HelpFormatter'>,
    conflict_handler='error',
    add_help=True
)

The constructor of ArgumentParser takes many alternative arguments that you should utilize to tweak a number of options of your CLIs. All of its arguments are non-obligatory, so essentially the most bare-bones parser which you could create outcomes from instantiating ArgumentParser with none arguments.

You’ll be taught extra concerning the arguments to the ArgumentParser constructor all through this tutorial, significantly within the part on customizing your argument parser. For now, you’ll be able to deal with the subsequent step in making a CLI with argparse. That step is so as to add arguments and choices by the parser object.

Including Arguments and Choices

So as to add arguments and choices to an argparse CLI, you’ll use the .add_argument() methodology of your ArgumentParser occasion. Observe that the tactic is widespread for arguments and choices. Do not forget that within the argparse terminology, arguments are referred to as positional arguments, and choices are referred to as non-obligatory arguments.

The primary argument to the .add_argument() methodology units the distinction between arguments and choices. This argument is recognized as both identify or flag. So, if you happen to present a identify, then you definitely’ll be defining an argument. In distinction, if you happen to use a flag, then you definitely’ll add an choice.

You’ve already labored with command-line arguments in argparse. So, contemplate the next enhanced model of your customized ls command, which provides an -l choice to the CLI:

 1# ls.py v2
 2
 3import argparse
 4import datetime
 5from pathlib import Path
 6
 7parser = argparse.ArgumentParser()
 8
 9parser.add_argument("path")
10
11parser.add_argument("-l", "--long", motion="store_true")
12
13args = parser.parse_args()
14
15target_dir = Path(args.path)
16
17if not target_dir.exists():
18    print("The goal listing does not exist")
19    increase SystemExit(1)
20
21def build_output(entry, lengthy=False):
22    if lengthy:
23        dimension = entry.stat().st_size
24        date = datetime.date.fromtimestamp(entry.stat().st_mtime).strftime(
25            "%b %d %m:%S"
26        )
27        return f"{dimension:>6d} {date} {entry.identify}"
28    return entry.identify
29
30for entry in target_dir.iterdir():
31    print(build_output(entry, lengthy=args.lengthy))

On this instance, line 11 creates an choice with the flags -l and --long. The syntactical distinction between arguments and choices is that choice names begin with - for shorthand flags and -- for lengthy flags.

Observe that on this particular instance, an motion argument set to "store_true" accompanies the -l or --long choice, which signifies that this feature will retailer a Boolean worth. If you happen to present the choice on the command line, then its worth will likely be True. If you happen to miss the choice, then its worth will likely be False. You’ll be taught extra concerning the motion argument to .add_argument() within the Setting the Motion Behind an Possibility part.

The build_output() operate on line 21 returns an in depth output when lengthy is True and a minimal output in any other case. The detailed output will include the dimensions, modification date, and identify of all of the entries within the goal listing. It makes use of instruments just like the Path.stat() and a datetime.date object with a customized string format.

Go forward and execute your program on pattern to test how the -l choice works:

$ python ls.py -l pattern/
  2609 Oct 28 10:00 lorem.md
   428 Oct 28 10:00 realpython.md
    83 Oct 28 10:00 hey.txt

Your new -l choice means that you can generate and show a extra detailed output concerning the content material of your goal listing.

Now that you know the way so as to add command-line arguments and choices to your CLIs, it’s time to dive into parsing these arguments and choices. That’s what you’ll discover within the following part.

Parsing Command-Line Arguments and Choices

Parsing the command-line arguments is one other vital step in any CLI app primarily based on argparse. When you’ve parsed the arguments, then you can begin taking motion in response to their values. In your customized ls command instance, the argument parsing occurs on the road containing the args = parser.parse_args() assertion.

This assertion calls the .parse_args() methodology and assigns its return worth to the args variable. The return worth of .parse_args() is a Namespace object containing all of the arguments and choices offered on the command line and their corresponding values.

Think about the next toy instance:

>>>

>>> from argparse import ArgumentParser

>>> parser = ArgumentParser()

>>> parser.add_argument("website")
_StoreAction(...)

>>> parser.add_argument("-c", "--connect", motion="store_true")
_StoreTrueAction(...)

>>> args = parser.parse_args(["Real Python", "-c"])
>>> args
Namespace(website='Actual Python', join=True)

>>> args.website
'Actual Python'
>>> args.join
True

The Namespace object that outcomes from calling .parse_args() on the command-line argument parser provides you entry to all of the enter arguments, choices, and their corresponding values by utilizing the dot notation. This fashion, you’ll be able to test the listing of enter arguments and choices to take actions in response to the consumer’s selections on the command line.

You’ll use this Namespace object in your utility’s essential code. That’s what you probably did beneath the for loop in your customized ls command instance.

Up so far, you’ve discovered about the principle steps for creating argparse CLIs. Now you’ll be able to take a while to be taught the fundamentals of easy methods to manage and construct a CLI utility in Python.

Setting Up Your CLI App’s Format and Construct System

Earlier than persevering with together with your argparse studying journey, you need to pause and consider how you’ll manage your code and lay out a CLI mission. First, you need to observe the next factors:

  • You possibly can create modules and packages to prepare your code.
  • You possibly can identify the core package deal of a Python app after the app itself.
  • You’ll identify every Python module in response to its particular content material or performance.
  • You possibly can add a __main__.py module to any Python package deal if you wish to make that package deal instantly executable.

With these concepts in thoughts and contemplating that the model-view-controller (MVC) sample is an efficient strategy to construction your functions, you should utilize the next listing construction when laying out a CLI mission:

hello_cli/
│
├── hello_cli/
│   ├── __init__.py
│   ├── __main__.py
│   ├── cli.py
│   └── mannequin.py
│
├── checks/
│   ├── __init__.py
│   ├── test_cli.py
│   └── test_model.py
│
├── pyproject.toml
├── README.md
└── necessities.txt

The hello_cli/ listing is the mission’s root listing. There, you’ll place the next recordsdata:

  • pyproject.toml is a TOML file that specifies the mission’s construct system and different configurations.
  • README.md supplies the mission description and directions for putting in and working the appliance. Including a descriptive and detailed README.md file to your initiatives is a greatest apply in programming, particularly if you happen to’re planning to launch the mission as an open-source answer.
  • necessities.txt supplies a traditional file that lists the mission’s exterior dependencies. You’ll use this file to routinely set up the dependencies utilizing pip with the -r choice.

Then you could have the hello_cli/ listing that holds the app’s core package deal, which accommodates the next modules:

  • __init__.py permits hello_cli/ as a Python package deal.
  • __main__.py supplies the appliance’s entry-point script or executable file.
  • cli.py supplies the appliance’s command-line interface. The code on this file will play the view-controller function within the MVC-based structure.
  • mannequin.py accommodates the code that helps the app’s essential functionalities. This code will play the mannequin function in your MVC structure.

You’ll even have a checks/ package deal containing recordsdata with unit checks to your app’s elements. On this particular mission structure instance, you could have test_cli.py for unit checks that test the CLI’s performance and test_model.py for unit checks that test your mannequin’s code.

The pyproject.toml file means that you can outline the app’s construct system in addition to many different basic configurations. Right here’s a minimal instance of easy methods to fill on this file to your pattern hello_cli mission:

# pyproject.toml

[build-system]
requires = ["setuptools>=64.0.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
identify = "hello_cli"
model = "0.0.1"
description = "My superior Hey CLI utility"
readme = "README.md"
authors = [{ name = "Real Python", email = "info@realpython.com" }]

[project.scripts]
hello_cli = "hello_cli.__main__:essential"

The [build-system] desk header units up setuptools as your app’s construct system and specifies which dependencies Python wants to put in for constructing your app. The [project] header supplies basic metadata to your utility. This metadata is fairly helpful if you need to publish your app to the Python package deal index (PyPI). Lastly, the [project.scripts] heading defines the entry level to your utility.

With this fast dive into laying out and constructing CLI initiatives, you’re able to proceed studying about argparse, particularly easy methods to customise your command-line argument parser.

Customizing Your Command-Line Argument Parser

In earlier sections, you discovered the fundamentals of utilizing Python’s argparse to implement command-line interfaces to your packages or functions. You additionally discovered easy methods to manage and lay out a CLI app mission following the MVC sample.

Within the following sections, you’ll dive deeper into many different neat options of argparse. Particularly, you’ll discover ways to use a few of the most helpful arguments within the ArgumentParser constructor, which is able to will let you customise the overall habits of your CLI apps.

Tweaking the Program’s Assist and Utilization Content material

Offering utilization directions and assist to the customers of your CLI functions is a greatest apply that’ll make your customers’ lives extra nice with an awesome consumer expertise (UX). On this part, you’ll discover ways to reap the benefits of some arguments of ArgumentParser to fine-tune how your CLI apps present assist and utilization messages to their customers. You’ll discover ways to:

  • Set this system’s identify
  • Outline this system’s description and epilog message
  • Show grouped assist for arguments and choices

To kick issues off, you’ll begin by setting your program’s identify and specifying how that identify will look within the context of a assist or utilization message.

Setting the Program’s Identify

By default, argparse makes use of the primary worth in sys.argv to set this system’s identify. This primary merchandise holds the identify of the Python file that you just’ve simply executed. This filename will look odd in a utilization message.

For instance, go forward and run your customized ls command with the -h choice:

$ python ls.py -h
utilization: ls.py [-h] [-l] path

positional arguments:
  path

choices:
  -h, --help  present this assist message and exit
  -l, --long

The highlighted line within the command’s output reveals that argparse is utilizing the filename ls.py as this system’s identify. This seems to be odd as a result of app names not often embody file extensions when displayed in utilization messages.

Fortuitously, you’ll be able to specify the identify of your program by utilizing the prog argument like within the following code snippet:

# ls.py v3

import argparse
import datetime
from pathlib import Path

parser = argparse.ArgumentParser(prog="ls")

# ...

for entry in target_dir.iterdir():
    print(build_output(entry, lengthy=args.lengthy))

With the prog argument, you specify this system identify that’ll be used within the utilization message. On this instance, you utilize the "ls" string. Now go forward and run your app once more:

$ python ls.py -h
utilization: ls [-h] [-l] path

positional arguments:
  path

choices:
  -h, --help  present this assist message and exit
  -l, --long

Nice! The app’s utilization message within the first line of this output reveals ls as a substitute of ls.py as this system’s identify.

Aside from setting this system’s identify, argparse enables you to outline the app’s description and epilog message. You’ll discover ways to do each within the following part.

Outline the Program’s Description and Epilog Message

You may as well outline a basic description to your utility and an epilog or closing message. To do that, you’ll use the description and epilog arguments, respectively. Go forward and replace the ls.py file with the next additions to the ArgumentParser constructor:

# ls.py v4

import argparse
import datetime
from pathlib import Path

parser = argparse.ArgumentParser(
    prog="ls",
    description="Listing the content material of a listing",
    epilog="Thanks for utilizing %(prog)s! :)",
)

# ...

for entry in target_dir.iterdir():
    print(build_output(entry, lengthy=args.lengthy))

On this replace, description means that you can present a basic description to your app. This description will show at first of the assistance message. The epilog argument enables you to outline some textual content as your app’s epilog or closing message. Observe which you could interpolate the prog argument into the epilog string utilizing the old-style string-formatting operator (%).

If you happen to run the app once more, then you definitely’ll get an output like the next:

$ python ls.py -h
utilization: ls [-h] [-l] path

Listing the content material of a listing

positional arguments:
  path

choices:
  -h, --help  present this assist message and exit
  -l, --long

Thanks for utilizing ls! :)

Now the output reveals the outline message proper after the utilization message and the epilog message on the finish of the assistance textual content.

Show Grouped Assist for Arguments and Choices

Assist teams are one other attention-grabbing characteristic of argparse. They will let you group associated instructions and arguments, which is able to show you how to manage the app’s assist message. To create these assist teams, you’ll use the .add_argument_group() methodology of ArgumentParser.

For instance, contemplate the next up to date model of your customized ls command:

# ls.py v5
# ...

parser = argparse.ArgumentParser(
    prog="ls",
    description="Listing the content material of a listing",
    epilog="Thanks for utilizing %(prog)s! :)",
)

basic = arg_parser.add_argument_group("basic output")
basic.add_argument("path")

detailed = arg_parser.add_argument_group("detailed output")
detailed.add_argument("-l", "--long", motion="store_true")

args = arg_parser.parse_args()

# ...

for entry in target_dir.iterdir():
    print(build_output(entry, lengthy=args.lengthy))

On this replace, you create a assist group for arguments and choices that show basic output and one other group for arguments and choices that show detailed output.

If you happen to run the app with the -h choice at your command line, then you definitely’ll get the next output:

python ls.py -h
utilization: ls [-h] [-l] path

Listing the content material of a listing

choices:
  -h, --help  present this assist message and exit

basic output:
  path

detailed output:
  -l, --long

Thanks for utilizing ls! :)

Now your app’s arguments and choices are conveniently grouped beneath descriptive headings within the assist message. This neat characteristic will show you how to present extra context to your customers and enhance their understanding of how the app works.

Offering World Settings for Arguments and Choices

Past customizing the utilization and assist messages, ArgumentParser additionally means that you can carry out just a few different attention-grabbing tweaks to your CLI apps. A few of these tweaks embody:

  • Defining a worldwide default worth for arguments and choices
  • Loading arguments and choices from an exterior file
  • Permitting or disallowing choice abbreviations

Generally, chances are you’ll have to specify a single international default worth to your app’s arguments and choices. You are able to do this by passing the default worth to argument_default on the decision to the ArgumentParser constructor.

This characteristic could also be solely not often helpful as a result of arguments and choices usually have a unique knowledge sort or which means, and it may be tough to discover a worth that fits all the necessities.

Nonetheless, a typical use case of argument_default is if you need to keep away from including arguments and choices to the Namespace object. On this state of affairs, you should utilize the SUPPRESS fixed because the default worth. This default worth will make it in order that solely the arguments and choices offered on the command line find yourself saved within the arguments Namespace.

For instance, go forward and modify your customized ls command as within the snippet under:

# ls.py v6

import argparse
import datetime
from pathlib import Path

parser = argparse.ArgumentParser(
    prog="ls",
    description="Listing the content material of a listing",
    epilog="Thanks for utilizing %(prog)s! :)",
    argument_default=argparse.SUPPRESS,
)

# ...

for entry in target_dir.iterdir():
    attempt:
        lengthy = args.lengthy
    besides AttributeError:
        lengthy = False
    print(build_output(entry, lengthy=lengthy))

By passing SUPPRESS to the ArgumentParser constructor, you stop non-supplied arguments from being saved within the arguments Namespace object. That’s why you must test if the -l or --long choice was truly handed earlier than calling build_output(). In any other case, your code will break with an AttributeError as a result of lengthy gained’t be current in args.

One other cool characteristic of ArgumentParser is that it means that you can load argument values from an exterior file. This risk turns out to be useful when you could have an utility with lengthy or sophisticated command-line constructs, and also you need to automate the method of loading argument values.

On this state of affairs, you’ll be able to retailer the argument values in an exterior file and ask your program to load them from it. To do this characteristic out, go forward and create the next toy CLI app:

# fromfile.py

import argparse

parser = argparse.ArgumentParser(fromfile_prefix_chars="@")

parser.add_argument("one")
parser.add_argument("two")
parser.add_argument("three")

args = parser.parse_args()

print(args)

Right here, you cross the @ image to the fromfile_prefix_chars argument of ArgumentParser. You then create three required arguments that have to be offered on the command line.

Now say that you just usually use this utility with the identical set of argument values. To facilitate and streamline your work, you’ll be able to create a file containing applicable values for all the required arguments, one per line, like within the following args.txt file:

With this file in place, now you can name your program and instruct it to load the values from the args.txt file like within the following command run:

$ python fromfile.py @args.txt
Namespace(one='first', two='second', three='third')

On this command’s output, you’ll be able to see that argparse has learn the content material of args.txt and sequentially assigned values to every argument of your fromfile.py program. All of the arguments and their values are efficiently saved within the Namespace object.

The flexibility to simply accept abbreviated choice names is one other cool characteristic of argparse CLIs. This characteristic is enabled by default and turns out to be useful when your program has lengthy choice names. For instance, contemplate the next program, which prints out the worth that you just specify on the command line beneath the --argument-with-a-long-name choice:

# abbreviate.py

import argparse

parser = argparse.ArgumentParser()

parser.add_argument("--argument-with-a-long-name")

args = parser.parse_args()

print(args.argument_with_a_long_name)

This program prints no matter your cross as an argument to the --argument-with-a-long-name choice. Go forward and run the next instructions to test how the Python argparse module handles abbreviations for you:

$ python abbreviate.py --argument-with-a-long-name 42
42

$ python abbreviate.py --argument 42
42

$ python abbreviate.py --a 42
42

These examples present how one can abbreviate the identify of the --argument-with-a-long-name choice and nonetheless get the app to work accurately. This characteristic is enabled by default. If you wish to disable it and forbid abbreviations, then you should utilize the allow_abbrev argument to ArgumentParser:

# abbreviate.py

import argparse

parser = argparse.ArgumentParser(allow_abbrev=False)

parser.add_argument("--argument-with-a-long-name")

args = parser.parse_args()

print(args.argument_with_a_long_name)

Setting allow_abbrev to False disables abbreviations in command-line choices. From this level on, you’ll have to supply the whole choice identify for this system to work accurately. In any other case, you’ll get an error:

$ python abbreviate.py --argument-with-a-long-name 42
42

$ python abbreviate.py --argument 42
utilization: abbreviate.py [-h] [--argument-with-a-long-name ...]
abbreviate.py: error: unrecognized arguments: --argument 42

The error message within the second instance tells you that the --argument choice isn’t acknowledged as a sound choice. To make use of the choice, you could present its full identify.

Advantageous-Tuning Your Command-Line Arguments and Choices

Up so far, you’ve discovered easy methods to customise a number of options of the ArgumentParser class to enhance the consumer expertise of your CLIs. Now you know the way to tweak the utilization and assist messages of your apps and easy methods to fine-tune some international points of command-line arguments and choices.

On this part, you’ll discover ways to customise a number of different options of your CLI’s command-line arguments and choices. On this case, you’ll be utilizing the .add_argument() methodology and a few of its most related arguments, together with motion, sort, nargs, default, assist, and some others.

Setting the Motion Behind an Possibility

If you add an choice or flag to a command-line interface, you’ll usually have to outline the way you need to retailer the choice’s worth within the Namespace object that outcomes from calling .parse_args(). To do that, you’ll use the motion argument to .add_argument(). The motion argument defaults to "retailer", which signifies that the worth offered for the choice at hand will likely be saved as is within the Namespace.

The motion argument can take one in every of a number of potential values. Right here’s the listing of those potential values and their meanings:

Allowed Worth Description
retailer Shops the enter worth to the Namespace object
store_const Shops a relentless worth when the choice is specified
store_true Shops the True Boolean worth when the choice is specified and shops False in any other case
store_false Shops False when the choice is specified and shops True in any other case
append Appends the present worth to a listing every time the choice is offered
append_const Appends a relentless worth to an inventory every time the choice is offered
rely Shops the variety of occasions the present choice has been offered
model Exhibits the app’s model and terminates the execution

On this desk, the values that embody the _const suffix of their names require you to supply the specified fixed worth utilizing the const argument within the name to the .add_argument() methodology. Equally, the model motion requires you to supply the app’s model by passing the model argument to .add_argument(). You must also notice that solely the retailer and append actions can and should take arguments on the command line.

To attempt these actions out, you’ll be able to create a toy app with the next implementation:

# actions.py

import argparse

parser = argparse.ArgumentParser()

parser.add_argument(
    "--name", motion="retailer"
)  # Equal to parser.add_argument("--name")
parser.add_argument("--pi", motion="store_const", const=3.14)
parser.add_argument("--is-valid", motion="store_true")
parser.add_argument("--is-invalid", motion="store_false")
parser.add_argument("--item", motion="append")
parser.add_argument("--repeated", motion="append_const", const=42)
parser.add_argument("--add-one", motion="rely")
parser.add_argument(
    "--version", motion="model", model="%(prog)s 0.1.0"
)

args = parser.parse_args()

print(args)

This program implements an choice for every sort of motion mentioned above. Then this system prints the ensuing Namespace of arguments. Right here’s a abstract of how these choices will work:

  • --name will retailer the worth handed, with none additional consideration.

  • --pi will routinely retailer the goal fixed when the choice is offered.

  • --is-valid will retailer True when offered and False in any other case. If you happen to want the alternative habits, use a store_false motion like --is-invalid on this instance.

  • --item will allow you to create an inventory of all of the values. You should repeat the choice for every worth. Below the hood, argparse will append the gadgets to an inventory named after the choice itself.

  • --repeated will work equally to --item. Nonetheless, it all the time appends the identical fixed worth, which it’s essential to present utilizing the const argument.

  • --add-one counts what number of occasions the choice is handed on the command line. The sort of choice is kind of helpful if you need to implement a number of verbosity ranges in your packages. For instance, -v can imply stage one in every of verbosity, -vv might point out stage two, and so forth.

  • --version reveals the app’s model and terminates the execution instantly. Observe that it’s essential to present the model quantity beforehand, which you are able to do by utilizing the model argument when creating the choice with .add_argument().

Go forward and run the script with the next command assemble to check out all these choices:

PS> python actions.py `
>   --name Python `
>   --pi `
>   --is-valid `
>   --is-invalid `
>   --item 1 --item 2 --item 3 `
>   --repeat --repeat --repeat `
>   --add-one --add-one --add-one
Namespace(
    identify='Python',
    pi=3.14,
    is_valid=True,
    is_invalid=False,
    merchandise=['1', '2', '3'],
    repeated=[42, 42, 42],
    add_one=3
)

PS> python actions.py --version
actions.py 0.1.0
$ python actions.py 
    --name Python 
    --pi 
    --is-valid 
    --is-invalid 
    --item 1 --item 2 --item 3 
    --repeat --repeat --repeat 
    --add-one --add-one --add-one
Namespace(
    identify='Python',
    pi=3.14,
    is_valid=True,
    is_invalid=False,
    merchandise=['1', '2', '3'],
    repeated=[42, 42, 42],
    add_one=3
)

$ python actions.py --version
actions.py 0.1.0

With this command, you present how all of the actions work and the way they’re saved within the ensuing Namespace object. The model motion is the final one that you just used, as a result of this feature simply reveals the model of this system after which ends the execution. It doesn’t get saved within the Namespace object.

Despite the fact that the default set of actions is kind of full, you even have the opportunity of creating customized actions by subclassing the argparse.Motion class. If you happen to determine to do that, then it’s essential to override the .__call__() methodology, which turns cases into callable objects. Optionally, you’ll be able to override the .__init__() and .format_usage() strategies relying in your wants.

To override the .__call__() methodology, you could be sure that the tactic’s signature consists of the parser, namespace, values, and option_string arguments.

Within the following instance, you implement a minimal and verbose retailer motion that you should utilize when constructing your CLI apps:

# custom_action.py

import argparse

class VerboseStore(argparse.Motion):
    def __call__(self, parser, namespace, values, option_string=None):
        print(f"Storing {values} within the {option_string} choice...")
        setattr(namespace, self.dest, values)

parser = argparse.ArgumentParser()

parser.add_argument("-n", "--name", motion=VerboseStore)

args = parser.parse_args()

print(args)

On this instance, you outline VerboseStore inheriting from argparse.Motion. You then override the .__call__() methodology to print an informative message and set the goal choice within the namespace of command-line arguments. Lastly, the app prints the namespace itself.

Go forward and run the next command to check out your customized motion:

$ python custom_action.py --name Python
Storing Python within the --name choice...
Namespace(identify='Python')

Nice! Your program now prints out a message earlier than storing the worth offered to the --name choice on the command line. Customized actions just like the one within the above instance will let you fine-tune how your packages’ choices are saved.

To proceed fine-tuning your argparse CLIs, you’ll discover ways to customise the enter worth of command-line arguments and choices within the following part.

Customizing Enter Values in Arguments and Choices

One other widespread requirement if you’re constructing CLI functions is to customise the enter values that arguments and choices will settle for on the command line. For instance, chances are you’ll require {that a} given argument settle for an integer worth, an inventory of values, a string, and so forth.

By default, any argument offered on the command line will likely be handled as a string. Fortuitously, argparse has inside mechanisms to test if a given argument is a sound integer, string, listing, and extra.

On this part, you’ll discover ways to customise the best way wherein argparse processes and shops enter values. Particularly, you’ll discover ways to:

  • Set the knowledge sort of enter values for arguments and choices
  • Take a number of enter values in arguments and choices
  • Present default values for arguments and choices
  • Outline an inventory of allowed enter values for arguments and choices

To kick issues off, you’ll begin by customizing the information sort that your arguments and choices will settle for on the command line.

Setting the Kind of Enter Values

When creating argparse CLIs, you’ll be able to outline the sort that you just need to use when storing command-line arguments and choices within the Namespace object. To do that, you should utilize the sort argument of .add_argument().

For instance, say that you just need to write a pattern CLI app for dividing two numbers. The app will take two choices, --dividend and --divisor. These choices will solely settle for integer numbers on the command line:

# divide.py

import argparse

parser = argparse.ArgumentParser()

parser.add_argument("--dividend", sort=int)
parser.add_argument("--divisor", sort=int)

args = parser.parse_args()

print(args.dividend / args.divisor)

On this instance, you set the kind of --dividend and --divisor to int. This setting will make your choices solely settle for legitimate integer values as enter. If the enter worth can’t be transformed to the int sort with out dropping data, then you definitely’ll get an error:

$ python divide.py --dividend 42 --divisor 2
21.0

$ python divide.py --dividend "42" --divisor "2"
21.0

$ python divide.py --dividend 42 --divisor 2.0
utilization: divide.py [-h] [--dividend DIVIDEND] [--divisor DIVISOR]
divide.py: error: argument --divisor: invalid int worth: '2.0'

$ python divide.py --dividend 42 --divisor two
utilization: divide.py [-h] [--dividend DIVIDEND] [--divisor DIVISOR]
divide.py: error: argument --divisor: invalid int worth: 'two'

The primary two examples work accurately as a result of the enter values are integer numbers. The third instance fails with an error as a result of the divisor is a floating-point quantity. The final instance additionally fails as a result of two isn’t a numeric worth.

Taking A number of Enter Values

Taking a number of values in arguments and choices could also be a requirement in a few of your CLI functions. By default, argparse assumes that you just’ll count on a single worth for every argument or choice. You possibly can modify this habits with the nargs argument of .add_argument().

The nargs argument tells argparse that the underlying argument can take zero or extra enter values relying on the particular worth assigned to nargs. In order for you the argument or choice to simply accept a set variety of enter values, then you’ll be able to set nargs to an integer quantity. If you happen to want extra versatile behaviors, then nargs has you coated as a result of it additionally accepts the next values:

Allowed Worth That means
? Accepts a single enter worth, which will be non-obligatory
* Takes zero or extra enter values, which will likely be saved in an inventory
+ Takes a number of enter values, which will likely be saved in an inventory
argparse.REMAINDER Gathers all of the values which can be remaining within the command line

It’s vital to notice that this listing of allowed values for nargs works for each command-line arguments and choices.

To start out attempting out the allowed values for nargs, go forward and create a level.py file with the next code:

# level.py

import argparse

parser = argparse.ArgumentParser()

parser.add_argument("--coordinates", nargs=2)

args = parser.parse_args()

print(args)

On this small app, you create a command-line choice referred to as --coordinates that takes two enter values representing the x and y Cartesian coordinates. With this script in place, go forward and run the next instructions:

$ python level.py --coordinates 2 3
Namespace(coordinates=['2', '3'])

$ python level.py --coordinates 2
utilization: level.py [-h] [--coordinates COORDINATES COORDINATES]
level.py: error: argument --coordinates: anticipated 2 arguments

$ python level.py --coordinates 2 3 4
utilization: level.py [-h] [--coordinates COORDINATES COORDINATES]
level.py: error: unrecognized arguments: 4

$ python level.py --coordinates
utilization: level.py [-h] [--coordinates COORDINATES COORDINATES]
level.py: error: argument --coordinates: anticipated 2 arguments

Within the first command, you cross two numbers as enter values to --coordinates. On this case, this system works accurately, storing the values in an inventory beneath the coordinates attribute within the Namespace object.

Within the second instance, you cross a single enter worth, and this system fails. The error message tells you that the app was anticipating two arguments, however you solely offered one. The third instance is fairly comparable, however in that case, you equipped extra enter values than required.

The ultimate instance additionally fails since you didn’t present enter values in any respect, and the --coordinates choice requires two values. On this instance, the 2 enter values are necessary.

To check out the * worth of nargs, say that you just want a CLI app that takes an inventory of numbers on the command line and returns their sum:

# sum.py

import argparse

parser = argparse.ArgumentParser()

parser.add_argument("numbers", nargs="*", sort=float)

args = parser.parse_args()

print(sum(args.numbers))

The numbers argument accepts zero or extra floating-point numbers on the command line since you’ve set nargs to *. Right here’s how this script works:

$ python sum.py 1 2 3
6.0

$ python sum.py 1 2 3 4 5 6
21.0

$ python sum.py
0

The primary two instructions present that numbers accepts an undetermined variety of values on the command line. These values will likely be saved in an inventory named after the argument itself within the Namespace object. If you happen to don’t cross any values to sum.py, then the corresponding listing of values will likely be empty, and the sum will likely be 0.

Subsequent up, you’ll be able to attempt the + worth of nargs with one other small instance. This time, say that you just want an app that accepts a number of recordsdata on the command line. You possibly can code this app like within the instance under:

# recordsdata.py

import argparse

parser = argparse.ArgumentParser()

parser.add_argument("recordsdata", nargs="+")

args = parser.parse_args()

print(args)

The recordsdata argument on this instance will settle for a number of values on the command line. You may give it a attempt by working the next instructions:

$ python recordsdata.py hey.txt
Namespace(recordsdata=['hello.txt'])

$ python recordsdata.py hey.txt realpython.md README.md
Namespace(recordsdata=['hello.txt', 'realpython.md', 'README.md'])

$ python recordsdata.py
utilization: recordsdata.py [-h] recordsdata [files ...]
recordsdata.py: error: the next arguments are required: recordsdata

The primary two examples present that recordsdata accepts an undefined variety of recordsdata on the command line. The final instance reveals which you could’t use recordsdata with out offering a file, as you’ll get an error. This habits forces you to supply no less than one file to the recordsdata argument.

The ultimate allowed worth for nargs is REMAINDER. This fixed means that you can seize the remaining values offered on the command line. If you happen to cross this worth to nargs, then the underlying argument will work as a bag that’ll collect all the additional enter values. As an train, go forward and discover how REMAINDER works by coding a small app by your self.

Despite the fact that the nargs argument provides you plenty of flexibility, generally it’s fairly difficult to make use of this argument accurately in a number of command-line choices and arguments. For instance, it may be onerous to reliably mix arguments and choices with nargs set to *, +, or REMAINDER in the identical CLI:

# cooking.py

import argparse

parser = argparse.ArgumentParser()

parser.add_argument("veggies", nargs="+")
parser.add_argument("fruits", nargs="*")

args = parser.parse_args()

print(args)

On this instance, the veggies argument will settle for a number of greens, whereas the fruits argument ought to settle for zero or extra fruits on the command line. Sadly, this instance doesn’t work as anticipated:

$ python cooking.py pepper tomato apple banana
Namespace(veggies=['pepper', 'tomato', 'apple', 'banana'], fruits=[])

The command’s output reveals that every one the offered enter values have been saved within the veggies attribute, whereas the fruits attribute holds an empty listing. This occurs as a result of the argparse parser doesn’t have a dependable strategy to decide which worth goes to which argument or choice. On this particular instance, you’ll be able to repair the issue by turning each arguments into choices:

# cooking.py

import argparse

parser = argparse.ArgumentParser()

parser.add_argument("--veggies", nargs="+")
parser.add_argument("--fruits", nargs="*")

args = parser.parse_args()

print(args)

With this minor replace, you’re making certain that the parser may have a safe strategy to parse the values offered on the command line. Go forward and run the next command to verify this:

$ python cooking.py --veggies pepper tomato --fruits apple banana
Namespace(veggies=['pepper', 'tomato'], fruits=['apple', 'banana'])

Now every enter worth has been saved within the appropriate listing within the ensuing Namespace. The argparse parser has used the choice names to accurately parse every equipped worth.

To keep away from points just like the one mentioned within the above instance, you need to all the time watch out when attempting to mix arguments and choices with nargs set to *, +, or REMAINDER.

Offering Default Values

The .add_argument() methodology can take a default argument that means that you can present an applicable default worth for particular person arguments and choices. This characteristic will be helpful if you want the goal argument or choice to all the time have a sound worth in case the consumer doesn’t present any enter on the command line.

For instance, get again to your customized ls command and say that you could make the command listing the content material of the present listing when the consumer doesn’t present a goal listing. You are able to do this by setting default to "." like within the code under:

# ls.py v7

import argparse
import datetime
from pathlib import Path

# ...

basic = parser.add_argument_group("basic output")
basic.add_argument("path", nargs="?", default=".")

# ...

The highlighted line on this code snippet does the magic. Within the name to .add_argument(), you utilize nargs with the query mark (?) as its worth. That you must do that as a result of all of the command-line arguments in argparse are required, and setting nargs to both ?, *, or + is the one strategy to skip the required enter worth. On this particular instance, you utilize ? since you want a single enter worth or none.

You then set default to the "." string, which represents the present working listing. With these updates, now you can run ls.py with out offering a goal listing. It’ll listing the content material of its default listing. To attempt it out, go forward and run the next instructions:

$ cd pattern/

$ python ../ls.py
lorem.md
realpython.md
hey.txt

Now your customized ls command lists the present listing’s content material if you happen to don’t present a goal listing on the command line. Isn’t that cool?

Specifying a Listing of Allowed Enter Values

One other attention-grabbing risk in argparse CLIs is which you could create a website of allowed values for a particular argument or choice. You are able to do this by offering an inventory of accepted values utilizing the selections argument of .add_argument().

Right here’s an instance of a small app with a --size choice that solely accepts just a few predefined enter values:

# dimension.py

import argparse

parser = argparse.ArgumentParser()

parser.add_argument("--size", selections=["S", "M", "L", "XL"], default="M")

args = parser.parse_args()

print(args)

On this instance, you utilize the selections argument to supply an inventory of allowed values for the --size choice. This setting will trigger the choice to solely settle for the predefined values. If you happen to attempt to use a price that’s not within the listing, then you definitely get an error:

$ python dimension.py --size S
Namespace(dimension='S')

$ python selections.py --size A
utilization: selections.py [-h] [--size {S,M,L,XL}]
selections.py: error: argument --size: invalid selection: 'A'
    (select from 'S', 'M', 'L', 'XL')

If you happen to use an enter worth from the listing of allowed values, then your app works accurately. If you happen to use an extraneous worth, then the app fails with an error.

The selections argument can maintain an inventory of allowed values, which will be of various knowledge sorts. For integer values, a helpful method is to make use of a spread of accepted values. To do that, you should utilize vary() like within the following instance:

# weekdays.py

import argparse

my_parser = argparse.ArgumentParser()

my_parser.add_argument("--weekday", sort=int, selections=vary(1, 8))

args = my_parser.parse_args()

print(args)

On this instance, the worth offered on the command line will likely be routinely checked in opposition to the vary object offered because the selections argument. Go forward and provides this instance a attempt by working the next instructions:

$ python days.py --weekday 2
Namespace(weekday=2)

$ python days.py --weekday 6
Namespace(weekday=6)

$ python days.py --weekday 9
utilization: days.py [-h] [--weekday {1,2,3,4,5,6,7}]
days.py: error: argument --weekday: invalid selection: 9
    (select from 1, 2, 3, 4, 5, 6, 7)

The primary two examples work accurately as a result of the enter quantity is within the allowed vary of values. Nonetheless, if the enter quantity is exterior the outlined vary, like within the final instance, then your app fails, displaying utilization and error messages.

Offering and Customizing Assist Messages in Arguments and Choices

As you already know, an awesome characteristic of argparse is that it generates automated utilization and assist messages to your functions. You possibly can entry these messages utilizing the -h or --help flag, which is included by default in any argparse CLI.

Up so far, you’ve discovered easy methods to present description and epilog messages to your apps. On this part, you’ll proceed bettering your app’s assist and utilization messages by offering enhanced messages for particular person command-line arguments and choices. To do that, you’ll use the assist and metavar arguments of .add_argument().

Return to your customized ls command and run the script with the -h swap to test its present output:

$ python ls.py -h
utilization: ls [-h] [-l] [path]

Listing the content material of a listing

choices:
  -h, --help  present this assist message and exit

basic output:
  path

detailed output:
  -l, --long

Thanks for utilizing ls! :)

This output seems to be good, and it’s a very good instance of how argparse saves you plenty of work by offering utilization and assist message out of the field.

Observe that solely the -h or --help choice reveals a descriptive assist message. In distinction, your individual arguments path and -l or --long don’t present a assist message. To repair that, you should utilize the assist argument.

Open your ls.py and replace it like within the following code:

# ls.py v8

import argparse
import datetime
from pathlib import Path

# ...

basic = parser.add_argument_group("basic output")
basic.add_argument(
    "path",
    nargs="?",
    default=".",
    assist="take the trail to the goal listing (default: %(default)s)",
)

detailed = parser.add_argument_group("detailed output")
detailed.add_argument(
    "-l",
    "--long",
    motion="store_true",
    assist="show detailed listing content material",
)

# ...

On this replace to ls.py, you utilize the assist argument of .add_argument() to supply particular assist messages to your arguments and choices.

Now go forward and run the app with the -h flag once more:

$ python ls.py -h
utilization: ls [-h] [-l] [path]

Listing the content material of a listing

choices:
  -h, --help  present this assist message and exit

basic output:
  path        take the trail to the goal listing (default: .)

detailed output:
  -l, --long  show detailed listing content material

Thanks for utilizing ls! :)

Now each path and -l present descriptive assist messages if you run the app with the -h flag. Observe that path consists of its default worth in its assist message, which supplies helpful data to your customers.

One other desired characteristic is to have a pleasant and readable utilization message in your CLI apps. The default utilization message of argparse is fairly good already. Nonetheless, you should utilize the metavar argument of .add_argument() to barely enhance it.

The metavar argument turns out to be useful when a command-line argument or choice accepts enter values. It means that you can give this enter worth a descriptive identify that the parser can use to generate the assistance message.

For instance of when to make use of metavar, return to your level.py instance:

# level.py

import argparse

parser = argparse.ArgumentParser()

parser.add_argument("--coordinates", nargs=2)

args = parser.parse_args()

print(args)

If you happen to run this utility out of your command line with the -h swap, then you definitely get an output that’ll seem like the next:

$ python level.py -h
utilization: level.py [-h] [--coordinates COORDINATES COORDINATES]

choices:
  -h, --help            present this assist message and exit
  --coordinates COORDINATES COORDINATES

By default, argparse makes use of the unique identify of command-line choices to designate their corresponding enter values within the utilization and assist messages, as you’ll be able to see within the highlighted strains. On this particular instance, the identify COORDINATES within the plural could also be complicated. Ought to your customers present the purpose’s coordinates two occasions?

You possibly can take away this ambiguity by utilizing the metavar argument:

# level.py

import argparse

parser = argparse.ArgumentParser()

parser.add_argument(
    "--coordinates",
    nargs=2,
    metavar=("X", "Y"),
    assist="take the Cartesian coordinates %(metavar)s",
)

args = parser.parse_args()

print(args)

On this instance, you utilize a tuple as the worth to metavar. The tuple accommodates the 2 coordinate names that folks generally use to designate a pair of Cartesian coordinates. You additionally present a customized assist message for --coordinates, together with a format specifier with the metavar argument.

If you happen to run the script with the -h flag, then you definitely get the next output:

$ python coordinates.py -h
utilization: coordinates.py [-h] [--coordinates X Y]

choices:
  -h, --help         present this assist message and exit
  --coordinates X Y  take the Cartesian coordinates ('X', 'Y')

Now your app’s utilization and assist messages are method clearer than earlier than. Now your customers will instantly know that they should present two numeric values, X and Y, for the --coordinates choice to work accurately.

Defining Mutually Unique Argument and Possibility Teams

One other attention-grabbing characteristic which you could incorporate into your argparse CLIs is the power to create mutually unique teams of arguments and choices. This characteristic turns out to be useful when you could have arguments or choices that may’t coexist in the identical command assemble.

Think about the next CLI app, which has --verbose and --silent choices that may’t coexist in the identical command name:

# teams.py

import argparse

parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)

group.add_argument("-v", "--verbose", motion="store_true")
group.add_argument("-s", "--silent", motion="store_true")

args = parser.parse_args()

print(args)

Having mutually unique teams for --verbose and --silent makes it inconceivable to make use of each choices in the identical command name:

$ python teams.py -v -s
utilization: teams.py [-h] (-v | -s)
teams.py: error: argument -s/--silent: not allowed with argument -v/--verbose

You possibly can’t specify the -v and -s flags in the identical command name. If you happen to attempt to do it, then you definitely get an error telling you that each choices aren’t allowed on the identical time.

Observe that the app’s utilization message showcases that -v and -s are mutually unique by utilizing the pipe image (|) to separate them. This fashion of presenting the choices have to be interpreted as use -v or -s, however not each.

Including Subcommands to Your CLIs

Some command-line functions reap the benefits of subcommands to supply new options and functionalities. Functions like pip, pyenv, Poetry, and git, that are fairly widespread amongst Python builders, make intensive use of subcommands.

For instance, if you happen to run pip with the --help swap, then you definitely’ll get the app’s utilization and assist message, which incorporates the whole listing of subcommands:

$ pip --help

Utilization:
  pip <command> [options]

Instructions:
  set up                     Set up packages.
  obtain                    Obtain packages.
  uninstall                   Uninstall packages.
  ...

To make use of one in every of these subcommands, you simply have to listing it after the app’s identify. For instance, the next command will listing all of the packages you’ve put in in your present Python surroundings:

$ pip listing
Package deal    Model
---------- -------
pip        x.y.z
setuptools x.y.z
   ...

Offering subcommands in your CLI functions is kind of a helpful characteristic. Fortuitously, argparse additionally supplies the required software to implement this characteristic. If you wish to arm your command-line apps with subcommands, then you should utilize the .add_subparsers() methodology of ArgumentParser.

For instance of utilizing .add_subparsers(), say you need to create a CLI app to carry out primary arithmetic operations, together with addition, subtraction, multiplication, and division. You need to implement these operations as subcommands in your app’s CLI.

To construct this app, you begin by coding the app’s core performance, or the arithmetic operations themselves. You then add the corresponding arguments to the app’s CLI:

 1# calc.py
 2
 3import argparse
 4
 5def add(a, b):
 6    return a + b
 7
 8def sub(a, b):
 9    return a - b
10
11def mul(a, b):
12    return a * b
13
14def div(a, b):
15    return a / b
16
17global_parser = argparse.ArgumentParser(prog="calc")
18subparsers = global_parser.add_subparsers(
19    title="subcommands", assist="arithmetic operations"
20)
21
22arg_template = {
23    "dest": "operands",
24    "sort": float,
25    "nargs": 2,
26    "metavar": "OPERAND",
27    "assist": "a numeric worth",
28}
29
30add_parser = subparsers.add_parser("add", assist="add two numbers a and b")
31add_parser.add_argument(**arg_template)
32add_parser.set_defaults(func=add)
33
34sub_parser = subparsers.add_parser("sub", assist="subtract two numbers a and b")
35sub_parser.add_argument(**arg_template)
36sub_parser.set_defaults(func=sub)
37
38mul_parser = subparsers.add_parser("mul", assist="multiply two numbers a and b")
39mul_parser.add_argument(**arg_template)
40mul_parser.set_defaults(func=mul)
41
42div_parser = subparsers.add_parser("div", assist="divide two numbers a and b")
43div_parser.add_argument(**arg_template)
44div_parser.set_defaults(func=div)
45
46args = global_parser.parse_args()
47
48print(args.func(*args.operands))

Right here’s a breakdown of how the code works:

  • Strains 5 to fifteen outline 4 capabilities that carry out the essential arithmetic operations of addition, subtraction, multiplication, and division. These capabilities will present the operations behind every of your app’s subcommands.

  • Line 17 defines the command-line argument parser as typical.

  • Strains 18 to twenty outline a subparser by calling .add_subparsers(). On this name, you present a title and a assist message.

  • Strains 22 to twenty-eight outline a template to your command-line arguments. This template is a dictionary containing delicate values for the required arguments of .add_argument(). Every argument will likely be referred to as operands and can include two floating-point values. Defining this template means that you can keep away from repetitive code when creating the command-line arguments.

  • Line 30 provides a parser to the subparser object. The identify of this subparser is add and can symbolize your subcommand for addition operations. The assist argument defines a assist message for this parser specifically.

  • Line 31 provides the operands command-line argument to the add subparser utilizing .add_argument() with the argument template. Observe that you could use the dictionary unpacking operator (**) to extract the argument template from arg_template.

  • Line 32 makes use of .set_defaults() to assign the add() callback operate to the add subparser or subcommand.

Strains 34 to 44 carry out actions just like these in strains 30 to 32 for the remainder of your three subcommands, sub, mul, and div. Lastly, line 48 calls the func attribute from args. This attribute will routinely name the operate related to the subcommand at hand.

Go forward and check out your new CLI calculator by working the next instructions:

$ python calc.py add 3 8
11.0

$ python calc.py sub 15 5
10.0

$ python calc.py mul 21 2
42.0

$ python calc.py div 12 2
6.0

$ python calc.py -h
utilization: calc [-h] {add,sub,mul,div} ...

choices:
  -h, --help         present this assist message and exit

subcommands:
  {add,sub,mul,div}  arithmetic operations
    add              add two numbers a and b
    sub              subtract two numbers a and b
    mul              multiply two numbers a and b
    div              divide two numbers a and b

$ python calc.py div -h
utilization: calc div [-h] OPERAND OPERAND

positional arguments:
  OPERAND     a numeric worth

choices:
  -h, --help  present this assist message and exit

Cool! All of your subcommands work as anticipated. They take two numbers and carry out the goal arithmetic operation with them. Observe that now you could have utilization and assist messages for the app and for every subcommand too.

Dealing with How Your CLI App’s Execution Terminates

When creating CLI functions, you’ll discover conditions wherein you’ll have to terminate the execution of an app due to an error or an exception. A standard apply on this state of affairs is to exit the app whereas emitting an error code or exit standing in order that different apps or the working system can perceive that the app has terminated due to an error in its execution.

Usually, if a command exits with a zero code, then it has succeeded. In the meantime, a nonzero exit standing signifies a failure. The downside of this technique is that whilst you have a single, well-defined strategy to point out success, you could have numerous methods to point failure, relying on the issue at hand.

Sadly, there’s no definitive customary for error codes or exit statuses. Working methods and programming languages use totally different kinds, together with decimal or hexadecimal numbers, alphanumeric codes, and even a phrase describing the error. Unix packages typically use 2 for command-line syntax errors and 1 for all different errors.

In Python, you’ll generally use integer values to specify the system exit standing of a CLI app. In case your code returns None, then the exit standing is zero, which is taken into account a profitable termination. Any nonzero worth means irregular termination. Most methods require the exit code to be within the vary from 0 to 127, and produce undefined outcomes in any other case.

When constructing CLI apps with argparse, you don’t want to fret about returning exit codes for profitable operations. Nonetheless, you need to return an applicable exit code when your app abruptly terminates its execution as a consequence of an error aside from command syntax errors, wherein case argparse does the give you the results you want out of the field.

The argparse module, particularly the ArgumentParser class, has two devoted strategies for terminating an app when one thing isn’t going properly:

Technique Description
.exit(standing=0, message=None) Terminates the app, returning the desired standing and printing message if given
.error(message) Prints a utilization message that includes the offered message and terminates the app with a standing code of 2

Each strategies print on to the customary error stream, which is devoted to error reporting. The .exit() methodology is suitable if you want full management over which standing code to return. Then again, the .error() methodology is internally utilized by argparse for command-line syntax errors, however you should utilize it at any time when it’s needed and applicable.

For instance of when to make use of these strategies, contemplate the next replace to your customized ls command:

# ls.py v9

import argparse
import datetime
from pathlib import Path

# ...

target_dir = Path(args.path)

if not target_dir.exists():
    parser.exit(1, message="The goal listing does not exist")

# ...

Within the conditional assertion that checks if the goal listing exists, as a substitute of utilizing increase SystemExit(1), you utilize ArgumentParser.exit(). This makes your code extra targeted on the chosen tech stack, which is the argparse framework.

To test how your app behaves now, go forward and run the next instructions:

PS> python ls.py .non_existing
The goal listing doesn't exist

PS> echo $LASTEXITCODE
1
$ python ls.py non_existing/
The goal listing does not exist

$ echo $?
1

The app terminates its execution instantly when the goal listing doesn’t exist. If you happen to’re on a Unix-like system, similar to Linux or macOS, then you’ll be able to examine the $? shell variable to verify that your app has returned 1 to sign an error in its execution. If you happen to’re on Home windows, then you’ll be able to test the contents of the $LASTEXITCODE variable.

Offering constant standing codes in your CLI functions is a greatest apply that’ll permit you and your customers to efficiently combine your app of their shell scripting and command pipes.

Conclusion

Now you already know what a command-line interface is and what its essential elements are, together with arguments, choices, and subcommands. You additionally discovered easy methods to create totally useful CLI functions utilizing the argparse module from the Python customary library.

On this tutorial, you’ve discovered easy methods to:

  • Get began with command-line interfaces
  • Manage and lay out a command-line mission in Python
  • Use Python’s argparse to create command-line interfaces
  • Customise most points of a CLI with some highly effective options of argparse

Understanding easy methods to write efficient and intuitive command-line interfaces is a superb talent to have as a developer. Writing good CLIs to your apps means that you can give your customers a pleasing consumer expertise whereas interacting together with your functions.

RELATED ARTICLES

Most Popular

Recent Comments