Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 39

Building Reusable CLIs with `argparse`

Recommended resources

Hello! Welcome to the fifth lesson in our "Python for DevOps Automation" module.

In our last lesson, we built a Python script to execute kubectl commands using the subprocess module. While powerful, that script had a limitation: its parameters, like the Kubernetes namespace, were hardcoded. To change them, you'd have to edit the script itself. This isn't scalable or user-friendly.

Today, we'll fix that. We are going to transform our scripts from simple, hardcoded programs into professional, reusable command-line interface (CLI) tools.

The learning outcome for this lesson is to develop a reusable command-line interface (CLI) tool for an automation task using argparse. This is a fundamental skill that allows you to build powerful, configurable automation that feels just like the native tools (git, aws, terraform) you use every day.

1. Why argparse?

As a developer, you might know that Python scripts can access command-line arguments through the sys.argv list. While this works for very simple cases, it quickly becomes cumbersome. You'd have to manually parse flags, handle different argument types, check for missing arguments, and generate help messages.

This is where Python's built-in argparse module comes in. It handles all of that boilerplate for you, allowing you to define the arguments your script expects and then simply use the parsed values.

To get a solid foundation on this, let's start with a short reading.

Build Command-Line Interfaces With Python's argparse

The article 'Build Command-Line Interfaces With Python's argparse' from Real Python provides an excellent introduction. It contrasts the manual sys.argv method with the robust argparse approach and outlines the four essential steps for creating any argparse CLI.

Please read the first two sections: 'Getting Started With CLIs in Python: sys.argv vs argparse': Focus on the comparison to understand the problem argparse solves. 'Creating a CLI With argparse': Pay close attention to the four core steps: importing, creating the parser, adding arguments, and parsing the arguments. This is the fundamental pattern we will use throughout the lesson.


2. The Building Blocks of a CLI

As the article explained, the process involves three main steps:

  1. Create an ArgumentParser instance.
  2. Use .add_argument() to define the arguments your tool accepts.
  3. Use .parse_args() to read the command-line input and get the values.

Let's dive into the details of .add_argument() by exploring positional and optional arguments.

Python argparse Module Example for CLI Tool Development
This image shows a Python script using `argparse` to define command-line arguments for connecting to a network device, along with the resulting auto-generated help message in a terminal.

Positional and Optional Arguments

You're already familiar with these concepts from tools like git and kubectl.

  • Positional arguments are required and their order matters (e.g., cp <source> <destination>).
  • Optional arguments (or flags) are prefixed with hyphens (- or --), are not required, and can appear in any order (e.g., ls -l -a).

The following video provides a clear, step-by-step walkthrough of how to define both types.

Python Argparse Module - Create CLI and Run Scripts With Command Line Arguments

The video 'Python Argparse Module' by Fabio Musanni is a fantastic, comprehensive guide. We'll start with the basics of setting up the parser and adding your first arguments.

Please watch the following segments: Setup and Parser Instance (00:00 - 02:02): See how to import argparse and create the ArgumentParser, which is the container for all your arguments. Positional Arguments (02:02 - 05:21): Focus on how a simple name like 'filename' creates a required, positional argument. Note how argparse automatically handles errors if it's missing. Optional Arguments (05:53 - 09:40): Understand how adding hyphens (e.g., '-c', '--copy') creates an optional argument. Pay attention to the metavar parameter for creating clearer help messages.


3. Customizing Arguments for DevOps Tasks

For DevOps automation, you often need more than just simple string arguments. You might need to handle numbers, boolean flags (--dry-run), version info, or a limited set of choices. argparse makes this straightforward.

Let's explore some of the most common customizations.

Types, Actions, and Defaults

The .add_argument() method takes several keyword arguments to control its behavior:

  • type: Coerces the input into a specific type, like int or float. If the conversion fails, argparse raises an error automatically.
  • action: Specifies how the argument should be handled. We'll focus on these:
    • 'store_true'/'store_false': For creating boolean flags. If the flag is present, it stores True (or False); if absent, it stores the opposite. Perfect for --verbose or --force.
    • 'version': To automatically print a version number and exit.
  • default: Provides a default value if an optional argument is not supplied.
  • choices: Restricts the argument's input to a specific list of values.
  • nargs: Allows an argument to accept multiple values.
  • help: The descriptive text that appears in the help message.

Let's continue with the video to see these powerful features in action.

Python Argparse Module - Create CLI and Run Scripts With Command Line Arguments

We'll now watch more segments of the same video to cover argument customization.

Watch the following segments, focusing on these key features: Actions (store_true, version) (09:40 - 13:34): See how action='store_true' creates a simple flag and action='version' adds professional version reporting to your tool. Type Validation (type) (13:34 - 16:24): Observe how type=int provides automatic input validation. The datetime example, while complex, shows the power of passing any callable to type. Defaults and Choices (16:24 - 18:53): Understand how default makes your tool easier to use and choices enforces valid inputs, preventing errors downstream. Multiple Values (nargs) (18:53 - 22:37): This is very useful. Pay attention to nargs='+' (one or more) and nargs='*' (zero or more), which are common for scripts that process multiple files or hosts.

The count Action for Verbosity

A very common pattern in CLIs is using -v, -vv, -vvv to increase verbosity. The action='count' makes implementing this trivial. It counts how many times a flag is specified.

Let's watch a quick, targeted video on this specific feature.

Argparse Tutorial - Python 2023 (Creating Your First CLI)

The video 'Argparse Tutorial - Python 2023' from Indently has a great, practical example of using action='count' to manage verbosity levels.

Watch the segment from 09:12 to 12:05. Focus on how action='count' is used with the -v flag and how the script uses an if/elif/else block to check the count and change its output accordingly.


4. Your Turn: Building a Reusable kubectl Wrapper

Now, let's put theory into practice. We're going to enhance the check_pods.py script you designed in the previous lesson. You will add a command-line interface to make it a flexible and reusable tool.

Scenario:
You want to turn your pod-checking script into a proper CLI tool that allows you to specify a namespace, filter by status, and check all namespaces.

Task:

  1. Start with your check_pods.py script from the previous lesson. If you don't have it, create a new one based on the logic described in that exercise.

  2. Add argparse to the script:

    • Import the argparse module.
    • Create an ArgumentParser with a helpful description, for example: 'A CLI tool to check the status of Kubernetes pods.'.
    • Add the following arguments:
      • An optional argument --namespace (short form -n). It should have a default value of 'default' and a helpful help message.
      • An optional argument --status. This will be used to filter for pods in a specific phase (e.g., 'Pending', 'Succeeded'). It should default to None.
      • A boolean flag --all-namespaces (short form -A). Use action='store_true' for this. Its help text should indicate that it overrides the --namespace argument.
    • Parse the arguments using parser.parse_args().
  3. Update your script's logic:

    • Modify the kubectl command list that you pass to subprocess.run().
    • If args.all_namespaces is True, add '-A' to your command list.
    • Otherwise, add '--namespace' and args.namespace to the command list.
    • After fetching and parsing the pod data, add another loop or conditional logic. If args.status is provided, your script should only report on pods whose status.phase matches the one provided by the user. If args.status is not provided, it should continue to report on any pod that isn't 'Running'.

Example Usage:




# Check for non-running pods in the 'default' namespace (uses the default)
python check_pods.py




# Check for non-running pods in the 'kube-system' namespace
python check_pods.py --namespace kube-system




# Check for pods with the status 'Pending' across all namespaces
python check_pods.py --all-namespaces --status Pending




# Get help
python check_pods.py --help

This exercise combines the subprocess skills from our last lesson with the argparse skills from this one, creating a genuinely useful DevOps tool.


Conclusion

Excellent work! You've just acquired a crucial skill for any DevOps engineer: turning a simple script into a professional, self-documenting, and configurable command-line tool. By defining arguments, you make your automation flexible and empower other team members to use it without having to read or modify the source code.

Key Takeaways:

  • argparse is the standard Python library for building robust CLIs.
  • You can define positional (required) and optional (--flag) arguments.
  • The .add_argument() method can be customized with type, default, and help to improve usability and robustness.
  • Actions like 'store_true' (for flags), 'count' (for verbosity), and 'version' provide powerful shortcuts for common CLI patterns.
  • Building a CLI around your automation scripts is a key step in making them reusable and professional.

In our next lesson, we will focus on implementing robust logging and structured error handling. A good CLI is not just about input; it's also about providing clear, controllable, and machine-readable output, which is exactly what a good logging strategy enables.