Movatterモバイル変換


[0]ホーム

URL:


— FREE Email Series —

🐍 Python Tricks 💌

Python Tricks Dictionary Merge

🔒 No spam. Unsubscribe any time.

Browse TopicsGuided Learning Paths
Basics Intermediate Advanced
apibest-practicescareercommunitydatabasesdata-sciencedata-structuresdata-vizdevopsdjangodockereditorsflaskfront-endgamedevguimachine-learningnumpyprojectspythontestingtoolsweb-devweb-scraping

Table of Contents

Recommended Video Course
Building Command Line Interfaces With argparse

How to Build Command Line Interfaces in Python With argparse

Build Command-Line Interfaces With Python's argparse

byLeodanis Pozo RamosPublication date Dec 14, 2024Reading time estimate 1h 4mintermediatepython

Table of Contents

Remove ads

Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding:Building Command Line Interfaces With argparse

When building Python command-line interfaces (CLI), Python’sargparse module offers a comprehensive solution. You can useargparse to create user-friendly command-line interfaces that parse arguments and options directly from the command line. This tutorial guides you through organizing CLI projects, adding arguments and options, and customizing your CLI’s behavior withargparse.

You’ll also learn about setting up command-line argument parsers, parsing arguments, and implementing advanced features like subcommands and mutually exclusive argument groups. By understanding howargparse handles errors and messages, you can create robust and intuitive CLIs for your Python applications.

By the end of this tutorial, you’ll understand that:

  • Command-line interfaces enable interaction with applications through terminals.
  • You cancreate CLIs in Python using the standard libraryargparse module.
  • argparse parses command-line arguments and generates help messages.
  • You cancustomize CLIs withargparse by defining argument types and actions.
  • Subcommands andmutually exclusive groups enhance CLI functionality.

To get the most out of this tutorial, you should be familiar with Python programming, including concepts such asobject-oriented programming,script development and execution, and Pythonpackages and modules. It’ll also be helpful if you’re familiar with general concepts and topics related to using a command line or terminal.

Get Your Code:Click here to download the free sample code that you’ll use to build command-line interfaces withargparse.

Take the Quiz: Test your knowledge with our interactive “Build Command-Line Interfaces With Python's argparse” quiz. You’ll receive a score upon completion to help you track your learning progress:


How to Build Command Line Interfaces in Python With argparse

Interactive Quiz

Build Command-Line Interfaces With Python's argparse

In this quiz, you'll test your understanding of creating command-line interfaces (CLIs) in Python using the argparse module. This knowledge is essential for creating user-friendly command-line apps, which are common in development, data science, and systems administration.

Getting to Know Command-Line Interfaces

Since the invention of computers, humans have always needed and found ways to interact and share information with these machines. The information exchange has flowed among humans,computer software, andhardware components. The shared boundary between any two of these elements is generically known as aninterface.

In software development, an interface is a special part of a given piece of software that allows interaction between components of a computer system. When it comes to human and software interaction, this vital component is known as theuser interface.

You’ll find different types of user interfaces in programming. Probably,graphical user interfaces (GUIs) are the most common today. However, you’ll also find apps and programs that providecommand-line interfaces (CLIs) for their users. In this tutorial, you’ll learn about CLIs and how to create them in Python.

Command-Line Interfaces (CLIs)

Command-line interfaces allow you to interact with an application or program through your operating system command line,terminal, or console.

To understand command-line interfaces and how they work, consider this practical example. Say that you have a directory calledsample containing three sample files. If you’re on aUnix-like operating system, such as Linux or macOS, go ahead and open a command-line window or terminal in the parent directory and then execute the following command:

Shell
$lssample/hello.txt     lorem.md      realpython.md

Thels Unix command lists the files and subdirectories contained in a target directory, which defaults to the current working directory. The above command call doesn’t display much information about the content ofsample. It only displays the filenames on the screen.

Note: If you’re on Windows, then you’ll have anls command that works similarly to the Unixls command. However, in its plain form, the command displays a different output:

Windows PowerShell
PS>ls.\sample\    Directory: C:\sampleMode                 LastWriteTime         Length Name----                 -------------         ------ -----a---          11/10/2022 10:06 AM             88 hello.txt-a---          11/10/2022 10:06 AM           2629 lorem.md-a---          11/10/2022 10:06 AM            429 realpython.md

The PowerShellls command issues a table containing detailed information on every file and subdirectory under your target directory. So, the upcoming examples won’t work as expected on Windows systems.

Suppose you want richer information about your directory and its content. In that case, you don’t need to look around for a program other thanls because this command has a full-featured command-line interface with a useful set ofoptions that you can use to customize the command’s behavior.

For example, go ahead and executels with the-l option:

Shell
$ls-lsample/total 24-rw-r--r--@ 1 user  staff    83 Aug 17 22:15 hello.txt-rw-r--r--@ 1 user  staff  2609 Aug 17 22:15 lorem.md-rw-r--r--@ 1 user  staff   428 Aug 17 22:15 realpython.md

The output ofls is quite different now. The command displays much more information about the files insample, including permissions, owner, group, date, and size. It also shows the total space that these files use on your computer’s disk.

Note: To get a detailed list of all the options thatls provides as part of its CLI, go ahead and run theman ls command in your command line or terminal.

This richer output results from using the-l option, which is part of the Unixls command-line interface and enables the detailed output format.

Commands, Arguments, Options, Parameters, and Subcommands

Throughout this tutorial, you’ll learn aboutcommands andsubcommands. You’ll also learn about command-linearguments,options, andparameters, so you should incorporate these terms into your tech vocabulary:

  • Command: A program or routine that runs at the command line or terminal window. You’ll typically identify a command with the name of the underlying program or routine.

  • Argument: A required or optional piece of information that a command uses to perform its intended action. Commands typically accept one or many arguments, which you can provide as a whitespace-separated or comma-separated list on your command line.

  • Option, also known asflag orswitch: An optional argument that modifies a command’s behavior. Options are passed to commands using a specific name, like-l in the previous example.

  • Parameter: An argument that an option uses to perform its intended operation or action.

  • Subcommand: A predefined name that can be passed to an application to run a specific action.

Consider the sample command construct from the previous section:

Shell
$ls-lsample/

In this example, you’ve combined the following components of a CLI:

  • ls: The command’s name or the app’s name
  • -l: An option, switch, or flag that enables detailed outputs
  • sample: An argument that provides additional information to the command’s execution

Now consider the following command construct, which showcases the CLI of Python’s package manager, known aspip:

Shell
$pipinstall-rrequirements.txt

This is a commonpip command construct, which you’ve probably seen before. It allows you to install the requirements of a given Python project using arequirements.txt file. In this example, you’re using the following CLI components:

  • pip: The command’s name
  • install: The name of a subcommand ofpip
  • -r: An option of theinstall subcommand
  • requirements.txt: An argument, specifically a parameter of the-r option

Now you know what command-line interfaces are and what their main parts or components are. It’s time to learn how to create your own CLIs in Python.

Getting Started With CLIs in Python:sys.argv vsargparse

Python comes with a couple of tools that you can use to write command-line interfaces for your programs and apps. If you need to quickly create a minimal CLI for a small program, then you can use theargv attribute from thesys module. This attribute automatically stores the arguments that you pass to a given program at the command line.

Usingsys.argv to Build a Minimal CLI

As an example of usingargv to create a minimal CLI, say that you need to write a small program that lists all the files in a given directory, similar to whatls does. In this situation, you can write something like this:

Pythonls_argv.py
importsysfrompathlibimportPathif(args_count:=len(sys.argv))>2:print(f"One argument expected, got{args_count-1}")raiseSystemExit(2)elifargs_count<2:print("You must specify the target directory")raiseSystemExit(2)target_dir=Path(sys.argv[1])ifnottarget_dir.is_dir():print("The target directory doesn't exist")raiseSystemExit(1)forentryintarget_dir.iterdir():print(entry.name)

This program implements a minimal CLI by manually processing the arguments provided at the command line, which are automatically stored insys.argv. The first item insys.argv is always the program’s name. The second item will be the target directory. The app shouldn’t accept more than one target directory, so theargs_count must not exceed2.

After checking the content ofsys.argv, you create apathlib.Path object to store the path to your target directory. If this directory doesn’t exist, then you inform the user and exit the app. Thefor loop lists the directory content, one entry per line.

If yourun the script from your command line, then you’ll get the following results:

Shell
$pythonls_argv.pysample/hello.txtlorem.mdrealpython.md$pythonls_argv.pyYou must specify the target directory$pythonls_argv.pysample/other_dir/One argument expected, got 2$pythonls_argv.pynon_existing/The target directory doesn't exist

Your program takes a directory as an argument and lists its content. If you run the command without arguments, then you get an error message. If you run the command with more than one target directory, you also get an error. Running the command with a nonexistent directory produces another error message.

Even though your program works okay, parsing command-line arguments manually using thesys.argv attribute isn’t a scalable solution for more complex CLI apps. If your app needs to take many more arguments and options, then parsingsys.argv will be a complex and error-prone task. You need something better, and you get it in Python’sargparse module.

Creating a CLI Withargparse

A much more convenient way to create CLI apps in Python is using theargparse module, which comes in thestandard library. This module was first released inPython 3.2 with PEP389 and is a quick way to create CLI apps in Python without installing a third-party library, such asTyper orClick.

This module was released as a replacement for the oldergetopt andoptparse modules because they lacked some important features.

Python’sargparse module allows you to:

  • Parse command-linearguments andoptions
  • Take avariable number of parameters in a single option
  • Providesubcommands in your CLIs

These features turnargparse into a powerful CLI framework that you can confidently rely on when creating your CLI applications. To use Python’sargparse, you’ll need to follow four straightforward steps:

  1. Importargparse.
  2. Create anargument parser by instantiatingArgumentParser.
  3. Addarguments andoptions to the parser using the.add_argument() method.
  4. Call.parse_args() on the parser to get theNamespace of arguments.

As an example, you can useargparse to improve yourls_argv.py script. Go ahead and createls.py with the following code:

Pythonls.py v1
importargparsefrompathlibimportPathparser=argparse.ArgumentParser()parser.add_argument("path")args=parser.parse_args()target_dir=Path(args.path)ifnottarget_dir.exists():print("The target directory doesn't exist")raiseSystemExit(1)forentryintarget_dir.iterdir():print(entry.name)

Your code has changed significantly with the introduction ofargparse. The most notable difference from the previous version is that theconditional statements to check the arguments provided by the user are gone. That’s becauseargparse automatically checks the presence of arguments for you.

In this new implementation, you first importargparse and create an argument parser. To create the parser, you use theArgumentParser class. Next, you define an argument calledpath to get the user’s target directory.

The next step is to call.parse_args() to parse the input arguments and get aNamespace object that contains all the user’s arguments. Note that now theargsvariable holds aNamespace object, which has a property for each argument that’s been gathered from the command line.

In this example, you only have one argument, calledpath. TheNamespace object allows you to accesspath using thedot notation onargs. The rest of your code remains the same as in the first implementation.

Now go ahead and run this new script from your command line:

Shell
$pythonls.pysample/lorem.mdrealpython.mdhello.txt$pythonls.pyusage: ls.py [-h] pathls.py: error: the following arguments are required: path$pythonls.pysample/other_dir/usage: ls.py [-h] pathls.py: error: unrecognized arguments: other_dir/$pythonls.pynon_existing/The target directory doesn't exist

The first commandprints the same output as your original script,ls_argv.py. In contrast, the second command displays output that’s quite different from inls_argv.py. The program now shows a usage message and issues an error telling you that you must provide thepath argument.

In the third command, you pass two target directories, but the app isn’t prepared for that. Therefore, it shows the usage message again and throws an error letting you know about the underlying problem.

Finally, if you run the script with a nonexistent directory as an argument, then you get an error telling you that the target directory doesn’t exist, so the program can’t do its work.

A new implicit feature is now available to you. Now your program accepts an optional-h flag. Go ahead and give it a try:

Shell
$pythonls.py-husage: ls.py [-h] pathpositional arguments:  pathoptions:  -h, --help  show this help message and exit

Great, now your program automatically responds to the-h or--help flag, displaying a help message with usage instructions for you. That’s a really neat feature, and you get it for free by introducingargparse into your code!

With this quick introduction to creating CLI apps in Python, you’re now ready to dive deeper into theargparse module and all its cool features.

Creating Command-Line Interfaces With Python’sargparse

You can use theargparse module to write user-friendly command-line interfaces for your applications and projects. This module allows you to define the arguments and options that your app will require. Thenargparse will take care of parsing those arguments and options ofsys.argv for you.

Another cool feature ofargparse is that it automatically generates usage and help messages for your CLI apps. The module also issues errors in response to invalid arguments and more.

Before diving deeper intoargparse, you need to know that the module’sdocumentation recognizes two different types of command-line arguments:

  1. Positional arguments, which you know as arguments
  2. Optional arguments, which you know as options, flags, or switches

In thels.py example,path is apositional argument. Such an argument is calledpositional because its relative position in the command construct defines its purpose.

Optional arguments aren’t mandatory. They allow you to modify the behavior of the command. In thels Unix command example, the-l flag is an optional argument, which makes the command display a detailed output.

With these concepts clear, you can kick things off and start building your own CLI apps with Python andargparse.

Creating a Command-Line Argument Parser

The command-line argument parser is the most important part of anyargparse CLI. All the arguments and options that you provide at the command line will pass through this parser, which will do the hard work for you.

To create a command-line argument parser withargparse, you need to instantiate theArgumentParser class:

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

Theconstructor ofArgumentParser takes many different arguments that you can use to tweak several features of your CLIs. All of its arguments are optional, so the most bare-bones parser that you can create results from instantiatingArgumentParser without any arguments.

You’ll learn more about the arguments to theArgumentParser constructor throughout this tutorial, particularly in the section oncustomizing your argument parser. For now, you can tackle the next step in creating a CLI withargparse. That step is to add arguments and options through the parser object.

Adding Arguments and Options

To add arguments and options to anargparse CLI, you’ll use the.add_argument() method of yourArgumentParser instance. Note that the method is common for arguments and options. Remember that in theargparse terminology, arguments are calledpositional arguments, and options are known asoptional arguments.

The first argument to the.add_argument() method sets the difference between arguments and options. This argument is identified as eithername orflag. So, if you provide aname, then you’ll be defining an argument. In contrast, if you use aflag, then you’ll add an option.

You’ve already worked with command-line arguments inargparse. So, consider the following enhanced version of your customls command, which adds an-l option to the CLI:

Pythonls.py v2
 1importargparse 2importdatetime 3frompathlibimportPath 4 5parser=argparse.ArgumentParser() 6 7parser.add_argument("path") 8 9parser.add_argument("-l","--long",action="store_true")1011args=parser.parse_args()1213target_dir=Path(args.path)1415ifnottarget_dir.exists():16print("The target directory doesn't exist")17raiseSystemExit(1)1819defbuild_output(entry,long=False):20iflong:21size=entry.stat().st_size22date=datetime.datetime.fromtimestamp(23entry.stat().st_mtime).strftime(24"%b%d %H:%M:%S"25)26returnf"{size:>6d}{date}{entry.name}"27returnentry.name2829forentryintarget_dir.iterdir():30print(build_output(entry,long=args.long))

In this example, line 9 creates an option with the flags-l and--long. The syntactical difference between arguments and options is that option names start with- for shorthand flags and-- for long flags.

Note that in this specific example, anaction argument set to"store_true" accompanies the-l or--long option, which means that this option will store aBoolean value. If you provide the option at the command line, then its value will beTrue. If you miss the option, then its value will beFalse. You’ll learn more about theaction argument to.add_argument() in theSetting the Action Behind an Option section.

Thebuild_output() function on line 19returns a detailed output whenlong isTrue and a minimal output otherwise. The detailed output will contain the size, modification date, and name of all the entries in the target directory. It uses tools like thePath.stat() and adatetime.datetime object with a customstring format.

Go ahead and execute your program onsample to check how the-l option works:

Shell
$pythonls.py-lsample/  2609 Oct 28 14:07:04 lorem.md   428 Oct 28 14:07:04 realpython.md    83 Oct 28 14:07:04 hello.txt

Your new-l option allows you to generate and display a more detailed output about the content of your target directory.

Now that you know how to add command-line arguments and options to your CLIs, it’s time to dive into parsing those arguments and options. That’s what you’ll explore in the following section.

Parsing Command-Line Arguments and Options

Parsing the command-line arguments is another important step in any CLI app based onargparse. Once you’ve parsed the arguments, then you can start taking action in response to their values. In your customls command example, the argument parsing happens on the line containing theargs = parser.parse_args() statement.

This statement calls the.parse_args() method and assigns its return value to theargs variable. The return value of.parse_args() is aNamespace object containing all the arguments and options provided at the command line and their corresponding values.

Consider the following toy example:

Python
>>>fromargparseimportArgumentParser>>>parser=ArgumentParser()>>>parser.add_argument("site")_StoreAction(...)>>>parser.add_argument("-c","--connect",action="store_true")_StoreTrueAction(...)>>>args=parser.parse_args(["Real Python","-c"])>>>argsNamespace(site='Real Python', connect=True)>>>args.site'Real Python'>>>args.connectTrue

TheNamespace object that results from calling.parse_args() on the command-line argument parser gives you access to all the input arguments, options, and their corresponding values by using thedot notation. This way, you can check the list of input arguments and options to take actions in response to the user’s choices at the command line.

You’ll use thisNamespace object in your application’s main code. That’s what you did under thefor loop in your customls command example.

Up to this point, you’ve learned about the main steps for creatingargparse CLIs. Now you can take some time to learn the basics of how to organize and build a CLI application in Python.

Setting Up Your CLI App’s Layout and Build System

Before continuing with yourargparse learning adventure, you should pause and think of how you would organize your code andlay out a CLI project. First, you should observe the following points:

  • You can createmodules and packages to organize your code.
  • You can name the core package of a Python app after the app itself.
  • You’ll name each Python module according to its specific content or functionality.
  • You can add a__main__.py module to any Python package if you want to make that package directly executable.

With these ideas in mind and considering that themodel-view-controller (MVC) pattern is an effective way to structure your applications, you can use the following directory structure when laying out a CLI project:

hello_cli/│├── hello_cli/│   ├── __init__.py│   ├── __main__.py│   ├── cli.py│   └── model.py│├── tests/│   ├── __init__.py│   ├── test_cli.py│   └── test_model.py│├── pyproject.toml├── README.md└── requirements.txt

Thehello_cli/ directory is the project’s root directory. There, you’ll place the following files:

  • pyproject.toml is aTOML file that specifies the project’sbuild system and otherconfigurations.
  • README.md provides the projectdescription andinstructions for installing and running the application. Adding a descriptive and detailedREADME.md file to your projects is a best practice in programming, especially if you’re planning to release the project as an open-source solution.
  • requirements.txt provides a conventional file that lists the project’sexternal dependencies. You’ll use this file to automatically install the dependencies usingpip with the-r option.

Then you have thehello_cli/ directory that holds the app’s core package, which contains the following modules:

  • __init__.py enableshello_cli/ as a Pythonpackage.
  • __main__.py provides the application’sentry-point script or executable file.
  • cli.py provides the application’s command-line interface. The code in this file will play theview-controller role in the MVC-based architecture.
  • model.py contains the code that supports the app’s main functionalities. This code will play themodel role in your MVC layout.

You’ll also have atests/ package containing files withunit tests for your app’s components. In this specific project layout example, you havetest_cli.py for unit tests that check the CLI’s functionality andtest_model.py for unit tests that check your model’s code.

Thepyproject.toml file allows you to define the app’s build system as well as many other general configurations. Here’s a minimal example of how to fill in this file for your samplehello_cli project:

TOML
# pyproject.toml[build-system]requires=["setuptools>=64.0.0","wheel"]build-backend="setuptools.build_meta"[project]name="hello_cli"version="0.0.1"description="My awesome Hello CLI application"readme="README.md"authors=[{name="Real Python",email="info@realpython.com"}][project.scripts]hello_cli="hello_cli.__main__:main"

The[build-system]table header sets upsetuptools as your app’sbuild system and specifies which dependencies Python needs to install for building your app. The[project] header provides general metadata for your application. This metadata is pretty useful when you want topublish your app to the Python package index (PyPI). Finally, the[project.scripts] heading defines the entry point to your application.

With this quick dive into laying out and building CLI projects, you’re ready to continue learning aboutargparse, especially how to customize your command-line argument parser.

Customizing Your Command-Line Argument Parser

In previous sections, you learned the basics of using Python’sargparse to implement command-line interfaces for your programs or applications. You also learned how to organize and lay out a CLI app project following the MVC pattern.

In the following sections, you’ll dive deeper into many other neat features ofargparse. Specifically, you’ll learn how to use some of the most useful arguments in theArgumentParser constructor, which will allow you to customize the general behavior of your CLI apps.

Tweaking the Program’s Help and Usage Content

Providing usage instructions and help to the users of your CLI applications is a best practice that’ll make your users’ lives more pleasant with a greatuser experience (UX). In this section, you’ll learn how to take advantage of some arguments ofArgumentParser to fine-tune how your CLI apps show help and usage messages to their users. You’ll learn how to:

  • Set the program’s name
  • Define the program’s description and epilog message
  • Display grouped help for arguments and options

To kick things off, you’ll start by setting your program’s name and specifying how that name will look in the context of a help or usage message.

Setting the Program’s Name

By default,argparse uses the first value insys.argv to set the program’s name. This first item holds the name of the Python file that you’ve just executed. This filename will look odd in a usage message.

As an example, go ahead and run your customls command with the-h option:

Shell
$pythonls.py-husage: ls.py [-h] [-l] pathpositional arguments:  pathoptions:  -h, --help  show this help message and exit  -l, --long

The highlighted line in the command’s output shows thatargparse is using the filenamels.py as the program’s name. This looks odd because app names rarely include file extensions when displayed in usage messages.

Fortunately, you can specify the name of your program by using theprog argument like in the following code snippet:

Pythonls.py v3
importargparseimportdatetimefrompathlibimportPathparser=argparse.ArgumentParser(prog="ls")# ...forentryintarget_dir.iterdir():print(build_output(entry,long=args.long))

With theprog argument, you specify the program name that’ll be used in the usage message. In this example, you use the"ls" string. Now go ahead and run your app again:

Shell
$pythonls.py-husage: ls [-h] [-l] pathpositional arguments:  pathoptions:  -h, --help  show this help message and exit  -l, --long

Great! The app’s usage message in the first line of this output showsls instead ofls.py as the program’s name.

Apart from setting the program’s name,argparse lets you define the app’s description and epilog message. You’ll learn how to do both in the following section.

Define the Program’s Description and Epilog Message

You can also define a general description for your application and an epilog or closing message. To do this, you’ll use thedescription andepilog arguments, respectively. Go ahead and update thels.py file with the following additions to theArgumentParser constructor:

Pythonls.py v4
importargparseimportdatetimefrompathlibimportPathparser=argparse.ArgumentParser(prog="ls",description="List the content of a directory",epilog="Thanks for using%(prog)s! :)",)# ...forentryintarget_dir.iterdir():print(build_output(entry,long=args.long))

In this update,description allows you to provide a general description for your app. This description will display at the beginning of the help message. Theepilog argument lets you define some text as your app’s epilog or closing message. Note that you can interpolate theprog argument into the epilog string using theold-style string-formatting operator (%).

Note: Help messages supportformat specifiers of the form%(specifier)s. These specifiers use the string formatting operator,%, rather than the popularf-strings. That’s because f-strings replace names with their values immediately as they run.

Therefore, insertingprog intoepilog in the call toArgumentParser above will fail with aNameError if you use an f-string.

If you run the app again, then you’ll get an output like the following:

Shell
$pythonls.py-husage: ls [-h] [-l] pathList the content of a directorypositional arguments:  pathoptions:  -h, --help  show this help message and exit  -l, --longThanks for using ls! :)

Now the output shows the description message right after the usage message and the epilog message at the end of the help text.

Display Grouped Help for Arguments and Options

Help groups are another interesting feature ofargparse. They allow you to group related commands and arguments, which will help you organize the app’s help message. To create these help groups, you’ll use the.add_argument_group() method ofArgumentParser.

As an example, consider the following updated version of your customls command:

Pythonls.py v5
# ...parser=argparse.ArgumentParser(prog="ls",description="List the content of a directory",epilog="Thanks for using%(prog)s! :)",)general=parser.add_argument_group("general output")general.add_argument("path")detailed=parser.add_argument_group("detailed output")detailed.add_argument("-l","--long",action="store_true")args=parser.parse_args()# ...forentryintarget_dir.iterdir():print(build_output(entry,long=args.long))

In this update, you create a help group for arguments and options that display general output and another group for arguments and options that display detailed output.

Note: In this specific example, grouping arguments like this may seem unnecessary. However, if your app has several arguments and options, then using help groups can significantly improve your user experience.

If you run the app with the-h option at your command line, then you’ll get the following output:

Shell
$pythonls.py-husage: ls [-h] [-l] pathList the content of a directoryoptions:  -h, --help  show this help message and exitgeneral output:  pathdetailed output:  -l, --longThanks for using ls! :)

Now your app’s arguments and options are conveniently grouped under descriptive headings in the help message. This neat feature will help you provide more context to your users and improve their understanding of how the app works.

Providing Global Settings for Arguments and Options

Beyond customizing the usage and help messages,ArgumentParser also allows you to perform a few other interesting tweaks to your CLI apps. Some of these tweaks include:

  • Defining a global default value for arguments and options
  • Loading arguments and options from an external file
  • Allowing or disallowing option abbreviations

Sometimes, you may need to specify a singleglobal default value for your app’s arguments and options. You can do this by passing the default value toargument_default on the call to theArgumentParser constructor.

This feature may be only rarely useful because arguments and options often have a different data type or meaning, and it can be difficult to find a value that suits all the requirements.

However, a common use case ofargument_default is when you want to avoid adding arguments and options to theNamespace object. In this situation, you can use theSUPPRESSconstant as the default value. This default value will make it so that only the arguments and options provided at the command line end up stored in the argumentsNamespace.

As an example, go ahead and modify your customls command as in the snippet below:

Pythonls.py v6
importargparseimportdatetimefrompathlibimportPathparser=argparse.ArgumentParser(prog="ls",description="List the content of a directory",epilog="Thanks for using%(prog)s! :)",argument_default=argparse.SUPPRESS,)# ...forentryintarget_dir.iterdir():try:long=args.longexceptAttributeError:long=Falseprint(build_output(entry,long=long))

By passingSUPPRESS to theArgumentParser constructor, you prevent non-supplied arguments from being stored in the argumentsNamespace object. That’s why you have to check if the-l or--long option was actually passed before callingbuild_output(). Otherwise, your code will break with anAttributeError becauselong won’t be present inargs.

Another cool feature ofArgumentParser is that it allows you toload argument values from an external file. This possibility comes in handy when you have an application with long or complicated command-line constructs, and you want to automate the process of loading argument values.

In this situation, you can store the argument values in an external file and ask your program to load them from it. To try this feature out, go ahead and create the following toy CLI app:

Pythonfromfile.py
importargparseparser=argparse.ArgumentParser(fromfile_prefix_chars="@")parser.add_argument("one")parser.add_argument("two")parser.add_argument("three")args=parser.parse_args()print(args)

Here, you pass the@ symbol to thefromfile_prefix_chars argument ofArgumentParser. Then you create three required arguments that must be provided at the command line.

Now say that you often use this application with the same set of argument values. To facilitate and streamline your work, you can create a file containing appropriate values for all the necessary arguments, one per line, like in the followingargs.txt file:

Text
firstsecondthird

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

Shell
$pythonfromfile.py@args.txtNamespace(one='first', two='second', three='third')

In this command’s output, you can see thatargparse has read the content ofargs.txt and sequentially assigned values to each argument of yourfromfile.py program. All the arguments and their values are successfully stored in theNamespace object.

The ability to acceptabbreviated option names is another cool feature ofargparse CLIs. This feature is enabled by default and comes in handy when your program has long option names. As an example, consider the following program, which prints out the value that you specify at the command line under the--argument-with-a-long-name option:

Pythonabbreviate.py
importargparseparser=argparse.ArgumentParser()parser.add_argument("--argument-with-a-long-name")args=parser.parse_args()print(args.argument_with_a_long_name)

This program prints whatever your pass as an argument to the--argument-with-a-long-name option. Go ahead and run the following commands to check how the Pythonargparse module handles abbreviations for you:

Shell
$pythonabbreviate.py--argument-with-a-long-name4242$pythonabbreviate.py--argument4242$pythonabbreviate.py--a4242

These examples show how you can abbreviate the name of the--argument-with-a-long-name option and still get the app to work correctly. This feature is enabled by default. If you want to disable it and forbid abbreviations, then you can use theallow_abbrev argument toArgumentParser:

Pythonabbreviate.py
importargparseparser=argparse.ArgumentParser(allow_abbrev=False)parser.add_argument("--argument-with-a-long-name")args=parser.parse_args()print(args.argument_with_a_long_name)

Settingallow_abbrev toFalse disables abbreviations in command-line options. From this point on, you’ll have to provide the complete option name for the program to work correctly. Otherwise, you’ll get an error:

Shell
$pythonabbreviate.py--argument-with-a-long-name4242$pythonabbreviate.py--argument42usage: abbreviate.py [-h] [--argument-with-a-long-name ...]abbreviate.py: error: unrecognized arguments: --argument 42

The error message in the second example tells you that the--argument option isn’t recognized as a valid option. To use the option, you need to provide its full name.

Fine-Tuning Your Command-Line Arguments and Options

Up to this point, you’ve learned how to customize several features of theArgumentParser class to improve the user experience of your CLIs. Now you know how to tweak the usage and help messages of your apps and how to fine-tune some global aspects of command-line arguments and options.

In this section, you’ll learn how to customize several other features of your CLI’s command-line arguments and options. In this case, you’ll be using the.add_argument() method and some of its most relevant arguments, includingaction,type,nargs,default,help, and a few others.

Setting the Action Behind an Option

When you add an option or flag to a command-line interface, you’ll often need to define how you want to store the option’s value in theNamespace object that results from calling.parse_args(). To do this, you’ll use theaction argument to.add_argument(). Theaction argument defaults to"store", which means that the value provided for the option at hand will be stored as is in theNamespace.

Theaction argument can take one of several possible values. Here’s the list of these possible values and their meanings:

Allowed ValueDescription
storeStores the input value to theNamespace object
store_constStores a constant value when the option is specified
store_trueStores theTrueBoolean value when the option is specified and storesFalse otherwise
store_falseStoresFalse when the option is specified and storesTrue otherwise
appendAppends the current value to alist each time the option is provided
append_constAppends a constant value to a list each time the option is provided
countStores the number of times the current option has been provided
versionShows the app’s version and terminates the execution

In this table, the values that include the_const suffix in their names require you to provide the desired constant value using theconst argument in the call to the.add_argument() method. Similarly, theversion action requires you to provide the app’s version by passing theversion argument to.add_argument(). You should also note that only thestore andappend actions can and must take arguments at the command line.

To try these actions out, you can create a toy app with the following implementation:

Pythonactions.py
importargparseparser=argparse.ArgumentParser()parser.add_argument("--name",action="store")# Equivalent to parser.add_argument("--name")parser.add_argument("--pi",action="store_const",const=3.14)parser.add_argument("--is-valid",action="store_true")parser.add_argument("--is-invalid",action="store_false")parser.add_argument("--item",action="append")parser.add_argument("--repeated",action="append_const",const=42)parser.add_argument("--add-one",action="count")parser.add_argument("--version",action="version",version="%(prog)s 0.1.0")args=parser.parse_args()print(args)

This program implements an option for each type ofaction discussed above. Then the program prints the resultingNamespace of arguments. Here’s a summary of how these options will work:

  • --name will store the value passed, without any further consideration.

  • --pi will automatically store the target constant when the option is provided.

  • --is-valid will storeTrue when provided andFalse otherwise. If you need the opposite behavior, use astore_false action like--is-invalid in this example.

  • --item will let you create a list of all the values. You must repeat the option for each value. Under the hood,argparse will append the items to a list named after the option itself.

  • --repeated will work similarly to--item. However, it always appends the same constant value, which you must provide using theconst argument.

  • --add-one counts how many times the option is passed at the command line. This type of option is quite useful when you want to implement several verbosity levels in your programs. For example,-v can mean level one of verbosity,-vv may indicate level two, and so on.

  • --version shows the app’s version and terminates the execution immediately. Note that you must provide the version number beforehand, which you can do by using theversion argument when creating the option with.add_argument().

Go ahead and run the script with the following command construct to try out all these options:

Windows PowerShell
PS>pythonactions.py`>--namePython`>--pi`>--is-valid`>--is-invalid`>--item1--item2--item3`>--repeat--repeat--repeat`>--add-one--add-one--add-oneNamespace(    name='Python',    pi=3.14,    is_valid=True,    is_invalid=False,    item=['1', '2', '3'],    repeated=[42, 42, 42],    add_one=3)PS>pythonactions.py--versionactions.py 0.1.0
Shell
$pythonactions.py\--namePython\--pi\--is-valid\--is-invalid\--item1--item2--item3\--repeat--repeat--repeat\--add-one--add-one--add-oneNamespace(    name='Python',    pi=3.14,    is_valid=True,    is_invalid=False,    item=['1', '2', '3'],    repeated=[42, 42, 42],    add_one=3)$pythonactions.py--versionactions.py 0.1.0

With this command, you show how all the actions work and how they’re stored in the resultingNamespace object. Theversion action is the last one that you used, because this option just shows the version of the program and then ends the execution. It doesn’t get stored in theNamespace object.

Even though the default set of actions is quite complete, you also have the possibility of creating custom actions by subclassing theargparse.Action class. If you decide to do this, then you must override the.__call__() method, which turnsinstances into callable objects. Optionally, you can override the.__init__() and.format_usage() methods depending on your needs.

To override the.__call__() method, you need to ensure that the method’s signature includes theparser,namespace,values, andoption_string arguments.

In the following example, you implement a minimal and verbosestore action that you can use when building your CLI apps:

Pythoncustom_action.py
importargparseclassVerboseStore(argparse.Action):def__call__(self,parser,namespace,values,option_string=None):print(f"Storing{values} in the{option_string} option...")setattr(namespace,self.dest,values)parser=argparse.ArgumentParser()parser.add_argument("-n","--name",action=VerboseStore)args=parser.parse_args()print(args)

In this example, you defineVerboseStore inheriting fromargparse.Action. Then you override the.__call__() method to print an informative message and set the target option in the namespace of command-line arguments. Finally, the app prints the namespace itself.

Go ahead and run the following command to try out your custom action:

Shell
$pythoncustom_action.py--namePythonStoring Python in the --name option...Namespace(name='Python')

Great! Your program now prints out a message before storing the value provided to the--name option at the command line. Custom actions like the one in the above example allow you to fine-tune how your programs’ options are stored.

To continue fine-tuning yourargparse CLIs, you’ll learn how to customize the input value of command-line arguments and options in the following section.

Customizing Input Values in Arguments and Options

Another common requirement when you’re building CLI applications is to customize the input values that arguments and options will accept at the command line. For example, you may require that a given argument accept an integer value, a list of values, a string, and so on.

By default, any argument provided at the command line will be treated as a string. Fortunately,argparse has internal mechanisms to check if a given argument is a valid integer, string, list, and more.

In this section, you’ll learn how to customize the way in whichargparse processes and stores input values. Specifically, you’ll learn how to:

  • Set thedata type of input values for arguments and options
  • Takemultiple input values in arguments and options
  • Providedefault values for arguments and options
  • Define a list ofallowed input values for arguments and options

To kick things off, you’ll start by customizing the data type that your arguments and options will accept at the command line.

Setting the Type of Input Values

When creatingargparse CLIs, you can define the type that you want to use when storing command-line arguments and options in theNamespaceobject. To do this, you can use thetype argument of.add_argument().

As an example, say that you want to write a sample CLI app for dividing twonumbers. The app will take two options,--dividend and--divisor. These options will only accept integer numbers at the command line:

Pythondivide.py
importargparseparser=argparse.ArgumentParser()parser.add_argument("--dividend",type=int)parser.add_argument("--divisor",type=int)args=parser.parse_args()print(args.dividend/args.divisor)

In this example, you set the type of--dividend and--divisor toint. This setting will make your options only accept valid integer values as input. If the input value can’t be converted to theint type without losing information, then you’ll get an error:

Shell
$pythondivide.py--dividend42--divisor221.0$pythondivide.py--dividend"42"--divisor"2"21.0$pythondivide.py--dividend42--divisor2.0usage: divide.py [-h] [--dividend DIVIDEND] [--divisor DIVISOR]divide.py: error: argument --divisor: invalid int value: '2.0'$pythondivide.py--dividend42--divisortwousage: divide.py [-h] [--dividend DIVIDEND] [--divisor DIVISOR]divide.py: error: argument --divisor: invalid int value: 'two'

The first two examples work correctly because the input values are integer numbers. The third example fails with an error because the divisor is a floating-point number. The last example also fails becausetwo isn’t a numeric value.

Taking Multiple Input Values

Taking multiple values in arguments and options may be a requirement in some of your CLI applications. By default,argparse assumes that you’ll expect a single value for each argument or option. You can modify this behavior with thenargs argument of.add_argument().

Thenargs argument tellsargparse that the underlying argument can take zero or more input values depending on the specific value assigned tonargs. If you want the argument or option to accept a fixed number of input values, then you can setnargs to an integer number. If you need more flexible behaviors, thennargs has you covered because it also accepts the following values:

Allowed ValueMeaning
?Accepts a single input value, which can be optional
*Takes zero or more input values, which will be stored in a list
+Takes one or more input values, which will be stored in a list
argparse.REMAINDERGathers all the values that are remaining in the command line

It’s important to note that this list of allowed values fornargs works for both command-line arguments and options.

To start trying out the allowed values fornargs, go ahead and create apoint.py file with the following code:

Pythonpoint.py
importargparseparser=argparse.ArgumentParser()parser.add_argument("--coordinates",nargs=2)args=parser.parse_args()print(args)

In this small app, you create a command-line option called--coordinates that takes two input values representing thex andyCartesian coordinates. With this script in place, go ahead and run the following commands:

Shell
$pythonpoint.py--coordinates23Namespace(coordinates=['2', '3'])$pythonpoint.py--coordinates2usage: point.py [-h] [--coordinates COORDINATES COORDINATES]point.py: error: argument --coordinates: expected 2 arguments$pythonpoint.py--coordinates234usage: point.py [-h] [--coordinates COORDINATES COORDINATES]point.py: error: unrecognized arguments: 4$pythonpoint.py--coordinatesusage: point.py [-h] [--coordinates COORDINATES COORDINATES]point.py: error: argument --coordinates: expected 2 arguments

In the first command, you pass two numbers as input values to--coordinates. In this case, the program works correctly, storing the values in a list under thecoordinates attribute in theNamespace object.

In the second example, you pass a single input value, and the program fails. The error message tells you that the app was expecting two arguments, but you only provided one. The third example is pretty similar, but in that case, you supplied more input values than required.

The final example also fails because you didn’t provide input values at all, and the--coordinates option requires two values. In this example, the two input values are mandatory.

To try out the* value ofnargs, say that you need a CLI app that takes a list of numbers at the command line and returns their sum:

Pythonsum.py
importargparseparser=argparse.ArgumentParser()parser.add_argument("numbers",nargs="*",type=float)args=parser.parse_args()print(sum(args.numbers))

Thenumbers argument accepts zero or more floating-point numbers at the command line because you’ve setnargs to*. Here’s how this script works:

Shell
$pythonsum.py1236.0$pythonsum.py12345621.0$pythonsum.py0

The first two commands show thatnumbers accepts an undetermined number of values at the command line. These values will be stored in a list named after the argument itself in theNamespace object. If you don’t pass any values tosum.py, then the corresponding list of values will be empty, and the sum will be0.

Next up, you can try the+ value ofnargs with another small example. This time, say that you need an app that accepts one or more files at the command line. You can code this app like in the example below:

Pythonfiles.py
importargparseparser=argparse.ArgumentParser()parser.add_argument("files",nargs="+")args=parser.parse_args()print(args)

Thefiles argument in this example will accept one or more values at the command line. You can give it a try by running the following commands:

Shell
$pythonfiles.pyhello.txtNamespace(files=['hello.txt'])$pythonfiles.pyhello.txtrealpython.mdREADME.mdNamespace(files=['hello.txt', 'realpython.md', 'README.md'])$pythonfiles.pyusage: files.py [-h] files [files ...]files.py: error: the following arguments are required: files

The first two examples show thatfiles accepts an undefined number of files at the command line. The last example shows that you can’t usefiles without providing a file, as you’ll get an error. This behavior forces you to provide at least one file to thefiles argument.

The final allowed value fornargs isREMAINDER. This constant allows you to capture the remaining values provided at the command line. If you pass this value tonargs, then the underlying argument will work as a bag that’ll gather all the extra input values. As an exercise, go ahead and explore howREMAINDER works by coding a small app by yourself.

Even though thenargs argument gives you a lot of flexibility, sometimes it’s pretty challenging to use this argument correctly in multiple command-line options and arguments. For example, it can be hard to reliably combine arguments and options withnargs set to*,+, orREMAINDER in the same CLI:

Pythoncooking.py
importargparseparser=argparse.ArgumentParser()parser.add_argument("veggies",nargs="+")parser.add_argument("fruits",nargs="*")args=parser.parse_args()print(args)

In this example, theveggies argument will accept one or more vegetables, while thefruits argument should accept zero or more fruits at the command line. Unfortunately, this example doesn’t work as expected:

Shell
$pythoncooking.pypeppertomatoapplebananaNamespace(veggies=['pepper', 'tomato', 'apple', 'banana'], fruits=[])

The command’s output shows that all the provided input values have been stored in theveggies attribute, while thefruits attribute holds an empty list. This happens because theargparse parser doesn’t have a reliable way to determine which value goes to which argument or option. In this specific example, you can fix the problem by turning both arguments into options:

Pythoncooking.py
importargparseparser=argparse.ArgumentParser()parser.add_argument("--veggies",nargs="+")parser.add_argument("--fruits",nargs="*")args=parser.parse_args()print(args)

With this minor update, you’re ensuring that the parser will have a secure way to parse the values provided at the command line. Go ahead and run the following command to confirm this:

Shell
$pythoncooking.py--veggiespeppertomato--fruitsapplebananaNamespace(veggies=['pepper', 'tomato'], fruits=['apple', 'banana'])

Now each input value has been stored in the correct list in the resultingNamespace. Theargparse parser has used the option names to correctly parse each supplied value.

To avoid issues similar to the one discussed in the above example, you should always be careful when trying to combine arguments and options withnargs set to*,+, orREMAINDER.

Providing Default Values

The.add_argument() method can take adefault argument that allows you to provide an appropriate default value for individual arguments and options. This feature can be useful when you need the target argument or option to always have a valid value in case the user doesn’t provide any input at the command line.

As an example, get back to your customls command and say that you need to make the command list the content of the current directory when the user doesn’t provide a target directory. You can do this by settingdefault to"." like in the code below:

Pythonls.py v7
importargparseimportdatetimefrompathlibimportPath# ...general=parser.add_argument_group("general output")general.add_argument("path",nargs="?",default=".")# ...

The highlighted line in this code snippet does the magic. In the call to.add_argument(), you usenargs with the question mark (?) as its value. You need to do this because all the command-line arguments inargparse are required, and settingnargs to either?,*, or+ is the only way to skip the required input value. In this specific example, you use? because you need a single input value or none.

Then you setdefault to the"." string, which represents the current working directory. With these updates, you can now runls.py without providing a target directory. It’ll list the content of its default directory. To try it out, go ahead and run the following commands:

Shell
$cdsample/$python../ls.pylorem.mdrealpython.mdhello.txt

Now your customls command lists the current directory’s content if you don’t provide a target directory at the command line. Isn’t that cool?

Specifying a List of Allowed Input Values

Another interesting possibility inargparse CLIs is that you can create a domain of allowed values for a specific argument or option. You can do this by providing a list of accepted values using thechoices argument of.add_argument().

Here’s an example of a small app with a--size option that only accepts a few predefined input values:

Pythonsize.py
importargparseparser=argparse.ArgumentParser()parser.add_argument("--size",choices=["S","M","L","XL"],default="M")args=parser.parse_args()print(args)

In this example, you use thechoices argument to provide a list of allowed values for the--size option. This setting will cause the option to only accept the predefined values. If you try to use a value that’s not in the list, then you get an error:

Shell
$pythonsize.py--sizeSNamespace(size='S')$pythonchoices.py--sizeAusage: choices.py [-h] [--size {S,M,L,XL}]choices.py: error: argument --size: invalid choice: 'A'    (choose from 'S', 'M', 'L', 'XL')

If you use an input value from the list of allowed values, then your app works correctly. If you use an extraneous value, then the app fails with an error.

Thechoices argument can hold a list of allowed values, which can be of different data types. For integer values, a useful technique is to use a range of accepted values. To do this, you can userange() like in the following example:

Pythonweekdays.py
importargparsemy_parser=argparse.ArgumentParser()my_parser.add_argument("--weekday",type=int,choices=range(1,8))args=my_parser.parse_args()print(args)

In this example, the value provided at the command line will be automatically checked against therange object provided as thechoices argument. Go ahead and give this example a try by running the following commands:

Shell
$pythondays.py--weekday2Namespace(weekday=2)$pythondays.py--weekday6Namespace(weekday=6)$pythondays.py--weekday9usage: days.py [-h] [--weekday {1,2,3,4,5,6,7}]days.py: error: argument --weekday: invalid choice: 9    (choose from 1, 2, 3, 4, 5, 6, 7)

The first two examples work correctly because the input number is in the allowed range of values. However, if the input number is outside the defined range, like in the last example, then your app fails, displaying usage and error messages.

Providing and Customizing Help Messages in Arguments and Options

As you already know, a great feature ofargparse is that it generates automatic usage and help messages for your applications. You can access these messages using the-h or--help flag, which is included by default in anyargparse CLI.

Up to this point, you’ve learned how to provide description and epilog messages for your apps. In this section, you’ll continue improving your app’s help and usage messages by providing enhanced messages for individual command-line arguments and options. To do this, you’ll use thehelp andmetavar arguments of.add_argument().

Go back to your customls command and run the script with the-h switch to check its current output:

Shell
$pythonls.py-husage: ls [-h] [-l] [path]List the content of a directoryoptions:  -h, --help  show this help message and exitgeneral output:  pathdetailed output:  -l, --longThanks for using ls! :)

This output looks nice, and it’s a good example of howargparse saves you a lot of work by providing usage and help message out of the box.

Note that only the-h or--help option shows a descriptive help message. In contrast, your own argumentspath and-l or--long don’t show a help message. To fix that, you can use thehelp argument.

Open yourls.py and update it like in the following code:

Pythonls.py v8
importargparseimportdatetimefrompathlibimportPath# ...general=parser.add_argument_group("general output")general.add_argument("path",nargs="?",default=".",help="take the path to the target directory (default:%(default)s)",)detailed=parser.add_argument_group("detailed output")detailed.add_argument("-l","--long",action="store_true",help="display detailed directory content",)# ...

In this update tols.py, you use thehelp argument of.add_argument() to provide specific help messages for your arguments and options.

Note: As you already know, help messages support format specifiers like%(prog)s. You can use most of the arguments toadd_argument() as format specifiers. For example,%(default)s,%(type)s, and so on.

Now go ahead and run the app with the-h flag again:

Shell
$pythonls.py-husage: ls [-h] [-l] [path]List the content of a directoryoptions:  -h, --help  show this help message and exitgeneral output:  path        take the path to the target directory (default: .)detailed output:  -l, --long  display detailed directory contentThanks for using ls! :)

Now bothpath and-l show descriptive help messages when you run the app with the-h flag. Note thatpath includes its default value in its help message, which provides valuable information to your users.

Another desired feature is to have a nice and readable usage message in your CLI apps. The default usage message ofargparse is pretty good already. However, you can use themetavar argument of.add_argument() to slightly improve it.

Themetavar argument comes in handy when a command-line argument or option accepts input values. It allows you to give this input value a descriptive name that the parser can use to generate the help message.

As an example of when to usemetavar, go back to yourpoint.py example:

Pythonpoint.py
importargparseparser=argparse.ArgumentParser()parser.add_argument("--coordinates",nargs=2)args=parser.parse_args()print(args)

If you run this application from your command line with the-h switch, then you get an output that’ll look like the following:

Shell
$pythonpoint.py-husage: point.py [-h] [--coordinates COORDINATES COORDINATES]options:  -h, --help            show this help message and exit  --coordinates COORDINATES COORDINATES

By default,argparse uses the original name of command-line options to designate their corresponding input values in the usage and help messages, as you can see in the highlighted lines. In this specific example, the nameCOORDINATES in the plural may be confusing. Should your users provide the point’s coordinates two times?

You can remove this ambiguity by using themetavar argument:

Pythonpoint.py
importargparseparser=argparse.ArgumentParser()parser.add_argument("--coordinates",nargs=2,metavar=("X","Y"),help="take the Cartesian coordinates%(metavar)s",)args=parser.parse_args()print(args)

In this example, you use a tuple as the value tometavar. The tuple contains the two coordinate names that people commonly use to designate a pair of Cartesian coordinates. You also provide a custom help message for--coordinates, including a format specifier with themetavar argument.

If you run the script with the-h flag, then you get the following output:

Shell
$pythoncoordinates.py-husage: coordinates.py [-h] [--coordinates X Y]options:  -h, --help         show this help message and exit  --coordinates X Y  take the Cartesian coordinates ('X', 'Y')

Now your app’s usage and help messages are way clearer than before. Now your users will immediately know that they need to provide two numeric values,X andY, for the--coordinates option to work correctly.

Defining Mutually Exclusive Argument and Option Groups

Another interesting feature that you can incorporate into yourargparse CLIs is the ability to create mutually exclusive groups of arguments and options. This feature comes in handy when you have arguments or options that can’t coexist in the same command construct.

Consider the following CLI app, which has--verbose and--silent options that can’t coexist in the same command call:

Pythongroups.py
importargparseparser=argparse.ArgumentParser()group=parser.add_mutually_exclusive_group(required=True)group.add_argument("-v","--verbose",action="store_true")group.add_argument("-s","--silent",action="store_true")args=parser.parse_args()print(args)

Having mutually exclusive groups for--verbose and--silent makes it impossible to use both options in the same command call:

Shell
$pythongroups.py-v-susage: groups.py [-h] (-v | -s)groups.py: error: argument -s/--silent: not allowed with argument -v/--verbose

You can’t specify the-v and-s flags in the same command call. If you try to do it, then you get an error telling you that both options aren’t allowed at the same time.

Note that the app’s usage message showcases that-v and-s are mutually exclusive by using the pipe symbol (|) to separate them. This way of presenting the options must be interpreted asuse-v or-s, but not both.

Adding Subcommands to Your CLIs

Some command-line applications take advantage of subcommands to provide new features and functionalities. Applications likepip,pyenv,Poetry, andgit, which are pretty popular among Python developers, make extensive use of subcommands.

For example, if you runpip with the--help switch, then you’ll get the app’s usage and help message, which includes the complete list of subcommands:

Shell
$pip--helpUsage:  pip <command> [options]Commands:  install                     Install packages.  download                    Download packages.  uninstall                   Uninstall packages.  ...

To use one of these subcommands, you just need to list it after the app’s name. For example, the following command will list all the packages you’ve installed in your current Python environment:

Shell
$piplistPackage    Version---------- -------pip        x.y.zsetuptools x.y.z   ...

Providing subcommands in your CLI applications is quite a useful feature. Fortunately,argparse also provides the required tool to implement this feature. If you want to arm your command-line apps with subcommands, then you can use the.add_subparsers() method ofArgumentParser.

As an example of using.add_subparsers(), say you want to create a CLI app to perform basic arithmetic operations, including addition, subtraction, multiplication, and division. You want to implement these operations as subcommands in your app’s CLI.

To build this app, you start by coding the app’s core functionality, or the arithmetic operations themselves. Then you add the corresponding arguments to the app’s CLI:

Pythoncalc.py
 1importargparse 2 3defadd(a,b): 4returna+b 5 6defsub(a,b): 7returna-b 8 9defmul(a,b):10returna*b1112defdiv(a,b):13returna/b1415global_parser=argparse.ArgumentParser(prog="calc")16subparsers=global_parser.add_subparsers(17title="subcommands",help="arithmetic operations"18)1920arg_template={21"dest":"operands",22"type":float,23"nargs":2,24"metavar":"OPERAND",25"help":"a numeric value",26}2728add_parser=subparsers.add_parser("add",help="add two numbers a and b")29add_parser.add_argument(**arg_template)30add_parser.set_defaults(func=add)3132sub_parser=subparsers.add_parser("sub",help="subtract two numbers a and b")33sub_parser.add_argument(**arg_template)34sub_parser.set_defaults(func=sub)3536mul_parser=subparsers.add_parser("mul",help="multiply two numbers a and b")37mul_parser.add_argument(**arg_template)38mul_parser.set_defaults(func=mul)3940div_parser=subparsers.add_parser("div",help="divide two numbers a and b")41div_parser.add_argument(**arg_template)42div_parser.set_defaults(func=div)4344args=global_parser.parse_args()4546print(args.func(*args.operands))

Here’s a breakdown of how the code works:

  • Lines 3 to 13 define four functions that perform the basic arithmetic operations of addition, subtraction, multiplication, and division. These functions will provide the operations behind each of your app’s subcommands.

  • Line 15 defines the command-line argument parser as usual.

  • Lines 16 to 18 define a subparser by calling.add_subparsers(). In this call, you provide a title and a help message.

  • Lines 20 to 26 define a template for your command-line arguments. This template is a dictionary containing sensitive values for the required arguments of.add_argument(). Each argument will be calledoperands and will consist of two floating-point values. Defining this template allows you to avoid repetitive code when creating the command-line arguments.

  • Line 28 adds a parser to the subparser object. The name of this subparser isadd and will represent your subcommand for addition operations. Thehelp argument defines a help message for this parser in particular.

  • Line 29 adds theoperands command-line argument to theadd subparser using.add_argument() with the argument template. Note that you need to use thedictionary unpacking operator (**) to extract the argument template fromarg_template.

  • Line 30 uses.set_defaults() to assign theadd() callback function to theadd subparser or subcommand.

Lines 32 to 42 perform actions similar to those in lines 28 to 30 for the rest of your three subcommands,sub,mul, anddiv. Finally, line 46 calls thefunc attribute fromargs. This attribute will automatically call the function associated with the subcommand at hand.

Go ahead and try out your new CLI calculator by running the following commands:

Shell
$pythoncalc.pyadd3811.0$pythoncalc.pysub15510.0$pythoncalc.pymul21242.0$pythoncalc.pydiv1226.0$pythoncalc.py-husage: calc [-h] {add,sub,mul,div} ...options:  -h, --help         show this help message and exitsubcommands:  {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$pythoncalc.pydiv-husage: calc div [-h] OPERAND OPERANDpositional arguments:  OPERAND     a numeric valueoptions:  -h, --help  show this help message and exit

Cool! All your subcommands work as expected. They take two numbers and perform the target arithmetic operation with them. Note that now you have usage and help messages for the app and for each subcommand too.

Handling How Your CLI App’s Execution Terminates

When creating CLI applications, you’ll find situations in which you’ll need to terminate the execution of an app because of an error or an exception. A common practice in this situation is to exit the app while emitting anerror code orexit status so that other apps or the operating system can understand that the app has terminated because of an error in its execution.

Typically, if a command exits with a zero code, then it has succeeded. Meanwhile, a nonzero exit status indicates a failure. The drawback of this system is that while you have a single, well-defined way to indicate success, you have various ways to indicate failure, depending on the problem at hand.

Unfortunately, there’s no definitive standard for error codes or exit statuses. Operating systems and programming languages use different styles, including decimal orhexadecimal numbers, alphanumeric codes, and even a phrase describing the error. Unix programs generally use2 for command-line syntax errors and1 for all other errors.

In Python, you’ll commonly use integer values to specify the system exit status of a CLI app. If your code returnsNone, then the exit status is zero, which is considered asuccessful termination. Any nonzero value meansabnormal termination. Most systems require the exit code to be in the range from0 to127, and produce undefined results otherwise.

When building CLI apps withargparse, you don’t need to worry about returning exit codes for successful operations. However, you should return an appropriate exit code when your app abruptly terminates its execution due to an error other than command syntax errors, in which caseargparse does the work for you out of the box.

Theargparse module, specifically theArgumentParser class, has two dedicated methods for terminating an app when something isn’t going well:

MethodDescription
.exit(status=0, message=None)Terminates the app, returning the specifiedstatus and printingmessage if given
.error(message)Prints a usage message that incorporates the providedmessage and terminates the app with a status code of2

Both methods print directly to thestandard error stream, which is dedicated to error reporting. The.exit() method is appropriate when you need complete control over which status code to return. On the other hand, the.error() method is internally used byargparse for command-line syntax errors, but you can use it whenever it’s necessary and appropriate.

As an example of when to use these methods, consider the following update to your customls command:

Pythonls.py v9
importargparseimportdatetimefrompathlibimportPath# ...target_dir=Path(args.path)ifnottarget_dir.exists():parser.exit(1,message="The target directory doesn't exist")# ...

In the conditional statement that checks if the target directory exists, instead of usingraise SystemExit(1), you useArgumentParser.exit(). This makes your code more focused on the selected tech stack, which is theargparse framework.

To check how your app behaves now, go ahead and run the following commands:

Windows PowerShell
PS>pythonls.py.\non_existing\Thetargetdirectorydoesn'texistPS>echo$LASTEXITCODE1
Shell
$pythonls.pynon_existing/The target directory doesn't exist$echo$?1

The app terminates its execution immediately when the target directory doesn’t exist. If you’re on a Unix-like system, such as Linux or macOS, then you can inspect the$? shell variable to confirm that your app has returned1 to signal an error in its execution. If you’re on Windows, then you can check the contents of the$LASTEXITCODE variable.

Providing consistent status codes in your CLI applications is a best practice that’ll allow you and your users to successfully integrate your app in their shell scripting and command pipes.

Conclusion

Now you know what a command-line interface is and what its main components are, including arguments, options, and subcommands. You also learned how to create fully functionalCLI applications using theargparse module from the Python standard library.

In this tutorial, you’ve learned how to:

  • Get started withcommand-line interfaces
  • Organize andlay out a command-line project in Python
  • Use Python’sargparse to createcommand-line interfaces
  • Customize most aspects of a CLI with somepowerful features ofargparse

Knowing how to write effective and intuitive command-line interfaces is a great skill to have as a developer. Writing good CLIs for your apps allows you to give your users a pleasant user experience while interacting with your applications.

Get Your Code:Click here to download the free sample code that you’ll use to build command-line interfaces withargparse.

Frequently Asked Questions

Now that you have some experience building command-line interfaces with Python’sargparse, you can use the questions and answers below to check your understanding and recap what you’ve learned.

These FAQs are related to the most important concepts you’ve covered in this tutorial. Click theShow/Hide toggle beside each question to reveal the answer.

You use theargparse module to create user-friendly command-line interfaces, allowing you to define the arguments your app requires and automatically generate help and usage messages.

You add arguments inargparse by calling the.add_argument() method on anArgumentParser instance, specifying the argument name and any options or flags it should have.

You handle optional arguments inargparse by using flags or switches that start with a dash (-) or double dash (--). You can specify their behavior using theaction parameter in.add_argument().

You can set a default value for an argument inargparse by using thedefault parameter in the.add_argument() method, specifying the value the argument should take if the user doesn’t provide it.

sys.argv provides a simple way to access command-line arguments as a list of strings, whileargparse offers a more powerful and flexible approach for parsing, validating, and handling command-line arguments.

Take the Quiz: Test your knowledge with our interactive “Build Command-Line Interfaces With Python's argparse” quiz. You’ll receive a score upon completion to help you track your learning progress:


How to Build Command Line Interfaces in Python With argparse

Interactive Quiz

Build Command-Line Interfaces With Python's argparse

In this quiz, you'll test your understanding of creating command-line interfaces (CLIs) in Python using the argparse module. This knowledge is essential for creating user-friendly command-line apps, which are common in development, data science, and systems administration.

Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding:Building Command Line Interfaces With argparse

🐍 Python Tricks 💌

Get a short & sweetPython Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.

Python Tricks Dictionary Merge

AboutLeodanis Pozo Ramos

Leodanis is a self-taught Python developer, educator, and technical writer with over 10 years of experience.

» More about Leodanis

Each tutorial at Real Python is created by a team of developers so that it meets our high quality standards. The team members who worked on this tutorial are:

MasterReal-World Python Skills With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

MasterReal-World Python Skills
With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

What Do You Think?

Rate this article:

What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.

Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students.Get tips for asking good questions andget answers to common questions in our support portal.


Looking for a real-time conversation? Visit theReal Python Community Chat or join the next“Office Hours” Live Q&A Session. Happy Pythoning!

Keep Learning

Related Topics:intermediatepython

Recommended Video Course:Building Command Line Interfaces With argparse

Related Tutorials:

Keep reading Real Python by creating a free account or signing in:

Already have an account?Sign-In

Almost there! Complete this form and click the button below to gain instant access:

How to Build Command Line Interfaces in Python With argparse

Build Command-Line Interfaces With Python's argparse (Source Code)

🔒 No spam. We take your privacy seriously.


[8]ページ先頭

©2009-2025 Movatter.jp