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:
#!/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:
$ ./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:
#!/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:
$ ./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:
#!/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:
- Accepts a list of service names to check
- Checks if each service is running
- Optionally attempts to restart failed services
Example usage:
$ ./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:
#!/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:
$ ./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:
#!/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:
$ ./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
- User-friendly error handling: Provide clear error messages when something goes wrong
- Progress feedback: For long-running operations, show progress bars or status updates
- Colorful output: Use libraries like
colorama
orrich
to make output more readable - Documentation: Include help text for all commands and options
- Configuration files: Allow users to set defaults in config files
- Testing: Write tests for your CLI applications
- 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
:
[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
- argparse documentation
- Click documentation
- Typer documentation
- Building Beautiful Command Line Interfaces with Python
- Python Packaging User Guide
Exercises
- Build a CLI tool that monitors disk usage and sends an alert when space is low
- Create a backup utility that takes directory paths as arguments
- Develop a multi-command application for managing Docker containers
- Extend the service checking tool to generate a report in multiple formats (text, JSON, HTML)
- 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! :)