Skip to main content

Python CLI Applications

Command Line Interface (CLI) applications are programs that users interact with through a terminal or command prompt rather than a graphical user interface. They're essential tools in DevOps workflows, allowing engineers to automate tasks, process data, and administer systems efficiently.

Why Build CLI Applications in Python?

Python is an excellent choice for building CLI applications because:

  • Readability: Python's clean syntax makes command-line tools easier to maintain
  • Cross-platform: Python CLI apps work on Windows, macOS, and Linux
  • Rich ecosystem: Many libraries exist to simplify CLI development
  • Integration capabilities: Python can interact with system commands and other tools

Basic CLI Structure

Let's start with a simple CLI application structure:

python
#!/usr/bin/env python3

def main():
print("Hello from my CLI application!")

if __name__ == "__main__":
main()

Save this as simple_cli.py, make it executable (chmod +x simple_cli.py on Unix-based systems), and run it:

bash
$ ./simple_cli.py
Hello from my CLI application!

Command-Line Arguments

Most CLI applications need to accept command-line arguments. Python's standard library includes the argparse module for this purpose.

Using argparse

Here's a basic example:

python
#!/usr/bin/env python3
import argparse

def main():
parser = argparse.ArgumentParser(description="A simple CLI application")
parser.add_argument("name", help="The name to greet")
parser.add_argument("--loud", "-l", action="store_true", help="Print in uppercase")

args = parser.parse_args()

greeting = f"Hello, {args.name}!"

if args.loud:
greeting = greeting.upper()

print(greeting)

if __name__ == "__main__":
main()

Running this script with arguments:

bash
$ ./greet_cli.py Alice
Hello, Alice!

$ ./greet_cli.py Bob --loud
HELLO, BOB!

$ ./greet_cli.py --help
usage: greet_cli.py [-h] [--loud] name

A simple CLI application

positional arguments:
name The name to greet

optional arguments:
-h, --help show this help message and exit
--loud, -l Print in uppercase

Building DevOps CLI Tools

DevOps engineers frequently build CLI tools to automate tasks. Let's create a simple tool that checks the status of services:

python
#!/usr/bin/env python3
import argparse
import subprocess
import sys

def check_service(service_name):
"""Check if a systemd service is running."""
try:
result = subprocess.run(
["systemctl", "is-active", service_name],
capture_output=True,
text=True,
check=False
)
status = result.stdout.strip()

if status == "active":
print(f"✅ Service {service_name} is running")
return True
else:
print(f"❌ Service {service_name} is not running (status: {status})")
return False
except Exception as e:
print(f"Error checking service {service_name}: {e}")
return False

def main():
parser = argparse.ArgumentParser(description="Check status of system services")
parser.add_argument("services", nargs="+", help="Service names to check")
parser.add_argument("--restart-failed", action="store_true",
help="Attempt to restart failed services")

args = parser.parse_args()

failed_services = []

for service in args.services:
if not check_service(service):
failed_services.append(service)

if failed_services and args.restart_failed:
print("\nAttempting to restart failed services...")
for service in failed_services:
print(f"Restarting {service}...")
subprocess.run(["sudo", "systemctl", "restart", service], check=False)
check_service(service)

if __name__ == "__main__":
main()

This script:

  1. Accepts a list of service names to check
  2. Checks if each service is running
  3. Optionally attempts to restart failed services

Example usage:

bash
$ ./check_services.py nginx postgresql
✅ Service nginx is running
❌ Service postgresql is not running (status: inactive)

$ ./check_services.py nginx postgresql --restart-failed
✅ Service nginx is running
❌ Service postgresql is not running (status: inactive)

Attempting to restart failed services...
Restarting postgresql...
✅ Service postgresql is running

Advanced CLI Libraries

While argparse is included in the standard library, more powerful libraries can enhance your CLI applications.

Click

Click is a powerful and intuitive package for creating beautiful command line interfaces:

python
#!/usr/bin/env python3
import click
import os

@click.group()
def cli():
"""File operations utility."""
pass

@cli.command()
@click.argument('path')
@click.option('--size', '-s', is_flag=True, help='Show file size')
@click.option('--count', '-c', is_flag=True, help='Show file count')
def analyze(path, size, count):
"""Analyze the specified directory."""
if not os.path.exists(path):
click.echo(f"Error: {path} does not exist")
return

if not os.path.isdir(path):
click.echo(f"Error: {path} is not a directory")
return

if size:
total_size = sum(os.path.getsize(os.path.join(path, f))
for f in os.listdir(path) if os.path.isfile(os.path.join(path, f)))
click.echo(f"Total size: {total_size/1024:.2f} KB")

if count:
file_count = len([f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))])
click.echo(f"File count: {file_count}")

if not size and not count:
click.echo(f"Directory: {path}")
for item in os.listdir(path):
click.echo(f"- {item}")

if __name__ == '__main__':
cli()

Example usage:

bash
$ ./file_tool.py analyze ./documents --size
Total size: 2345.78 KB

$ ./file_tool.py analyze ./documents --count
File count: 17

$ ./file_tool.py analyze ./documents
Directory: ./documents
- report.pdf
- notes.txt
- meeting_minutes.docx
- ...

Typer

Typer is a library for building CLI applications that uses Python type hints:

python
#!/usr/bin/env python3
import typer
import requests
from typing import Optional
import time

app = typer.Typer()

@app.command()
def ping(
url: str,
count: int = 3,
interval: float = 1.0,
timeout: Optional[float] = 5.0
):
"""Ping a website and check response time."""
successful = 0
times = []

typer.echo(f"Pinging {url} {count} times...")

for i in range(count):
try:
start_time = time.time()
response = requests.get(url, timeout=timeout)
end_time = time.time()

if response.status_code == 200:
elapsed = (end_time - start_time) * 1000 # ms
times.append(elapsed)
successful += 1
typer.echo(f"Ping {i+1}: Success - Response time: {elapsed:.2f}ms")
else:
typer.echo(f"Ping {i+1}: Failed - Status code: {response.status_code}")
except requests.exceptions.RequestException as e:
typer.echo(f"Ping {i+1}: Error - {str(e)}")

if i < count - 1:
time.sleep(interval)

typer.echo(f"\nSummary:")
typer.echo(f"- Successful: {successful}/{count} ({successful/count*100:.1f}%)")

if times:
typer.echo(f"- Avg response time: {sum(times)/len(times):.2f}ms")
typer.echo(f"- Min response time: {min(times):.2f}ms")
typer.echo(f"- Max response time: {max(times):.2f}ms")

if __name__ == "__main__":
app()

Example usage:

bash
$ ./ping_tool.py ping https://www.example.com --count 4 --interval 0.5
Pinging https://www.example.com 4 times...
Ping 1: Success - Response time: 145.23ms
Ping 2: Success - Response time: 138.67ms
Ping 3: Success - Response time: 152.11ms
Ping 4: Success - Response time: 141.89ms

Summary:
- Successful: 4/4 (100.0%)
- Avg response time: 144.48ms
- Min response time: 138.67ms
- Max response time: 152.11ms

Best Practices for Python CLI Applications

  1. User-friendly error handling: Provide clear error messages when something goes wrong
  2. Progress feedback: For long-running operations, show progress bars or status updates
  3. Colorful output: Use libraries like colorama or rich to make output more readable
  4. Documentation: Include help text for all commands and options
  5. Configuration files: Allow users to set defaults in config files
  6. Testing: Write tests for your CLI applications
  7. Packaging: Make your CLI tools installable with pip

Making Your CLI Application Installable

To make your CLI tool available system-wide, create a proper Python package:

my_tool/
├── pyproject.toml
├── README.md
└── src/
└── my_tool/
├── __init__.py
└── main.py

In your pyproject.toml:

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

[project]
name = "my-tool"
version = "0.1.0"
description = "A helpful CLI tool"
authors = [
{name = "Your Name", email = "[email protected]"}
]
readme = "README.md"
requires-python = ">=3.7"
dependencies = [
"requests>=2.25.1",
"typer>=0.4.0",
]

[project.scripts]
my-tool = "my_tool.main:app"

After installing this package with pip install -e ., you can run my-tool from anywhere in your system.

Summary

Python CLI applications are powerful tools for DevOps engineers, allowing automation of various tasks through simple command-line interfaces. We've covered:

  • Basic CLI application structure
  • Using argparse for processing command-line arguments
  • Building practical DevOps tools
  • Advanced CLI libraries like Click and Typer
  • Best practices and packaging for distribution

These skills enable you to create efficient, user-friendly tools that can streamline your DevOps workflows and automate repetitive tasks.

Additional Resources

Exercises

  1. Build a CLI tool that monitors disk usage and sends an alert when space is low
  2. Create a backup utility that takes directory paths as arguments
  3. Develop a multi-command application for managing Docker containers
  4. Extend the service checking tool to generate a report in multiple formats (text, JSON, HTML)
  5. Build a deployment script that takes configuration options and updates a service


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)