Command Line Apps in Python

on
Aug 18, 2018
in

Python is a great language for automation. Ansible, SaltStack and Fabric are Python based. Most DevOps tools provide an SDK for Python.
Cloud providers like AWS, Google Cloud Platform (GCP) and
Azure provide an SDK for Python. The Python standard library provides
capabilities that support automation. Python is a good alternative to bash. Python scripts can be tested and reused.
Python scripts that are available as a Command Line Interface (CLI) application enable DevOps. It is very easy to create
a CLI in Python. Lets see how that works!

Buckets

We will create a small application that uses the AWS SDK for Python. The
application is available in Github and is called buckets. We will incrementally
add features to buckets. This way we learn how to create CLI apps.

Hello-Buckets

Python supports creating CLI apps. The package click enables Python programmers
to create beautiful CLI apps. The branch 01-hello-buckets
shows what is necessary to create a simple CLI.

import click

@click.command()
def main():
    """Bucket, your interface to S3"""
    print("Hello, I am Buckets")

The simplest CLI app contains a single function that has been decorated with @click.command(). The annotation tells
click to create a CLI app. When we run python buckets/buckets.py --help it prints the following:

Usage: buckets.py [OPTIONS]

  Bucket, your interface to S3

Options:
  --help  Show this message and exit.

And when we run the application python buckets/buckets.py it prints:

Hello, I am Buckets

Package, Install and Run

A CLI app should run standalone. The project 01-hello-buckets
contains setup.py that is used to package the CLI app.
With make dist we create an installation binary. With make install we install the binary. We can now type buckets or buckets --help
to run the CLI app. Buckets can now be used in DevOps pipelines. With make uninstall we uninstall buckets.

AWS S3

AWS Simple Storage Service (S3) is a massive storage service. AWS provides an SDK called boto3
to interface with S3. The branch 02-boto-buckets shows how to create a command
to display all S3 buckets in your AWS account.

import click
import boto3

client = boto3.client('s3')

@click.command()
def main():
    """Bucket, your interface to S3"""
    rows = []
    rows.append(['BucketName', 'CreationDate'])
    for bucket in client.list_buckets()['Buckets']:
        rows.append([bucket['Name'], bucket['CreationDate'].__str__()])

    print(render_table(rows))

After make dist and make install, buckets retrieves all buckets from S3 and displays them:

BucketName                                       CreationDate
===============================================  =========================
                                   dennisvriend  2018-08-15 19:47:35+00:00
                               dennisvriend.com  2018-06-04 18:56:29+00:00

Multiple Commands

The branch 03-multiple-commands shows how to create multiple commands.
Buckets now supports ls, du and count as commands.

import click
import boto3

client = boto3.client('s3')

@click.group()
def main():
    """Bucket, your interface to S3"""

@main.command()
def ls():
    """Print all buckets"""
    rows = []
    rows.append(['BucketName', 'CreationDate'])
    for bucket in client.list_buckets()['Buckets']:
        rows.append([bucket['Name'], bucket['CreationDate'].__str__()])

    print(render_table(rows))

@main.command()
def du():
    """Print usage per bucket"""
    rows = []
    rows.append(['BucketName', 'CreationDate', 'Number of objects', 'Bytes'])
    for bucket in client.list_buckets()['Buckets']:
        num_objects, total_bytes = get_bucket_size(bucket['Name'])
        rows.append([bucket['Name'], bucket['CreationDate'].__str__(), num_objects, total_bytes])

    print(render_table(rows))

@main.command()
def count():
    """Count the number of buckets"""
    sum = 0
    for _ in client.list_buckets()['Buckets']:
        sum += 1
    print(f'{str(sum)} buckets')

When we run python buckets/buckets.py --help it prints the following:

Usage: buckets.py [OPTIONS] COMMAND [ARGS]...

  Bucket, your interface to S3

Options:
  --help  Show this message and exit.

Commands:
  count  Count the number of buckets
  du     Print usage per bucket
  ls     Print all buckets

When we run python buckets/buckets.py count, the command count is executed. The method main has been decorated with

@click.group(). This tells click to create a CLI app. We can add multiple commands by decorating
other methods. Notice that we annotate the other methods with the name @main.command(). Click will add these commands to the

group main. By adding commands to the group you can add multiple commands and execute them via the CLI.

Conclusion

It is very easy to create CLI apps in Python by decorating functions. We use the @click.command() for a simple CLI. We use
the @click.group() to decorate a method – eg. main – to be a placeholder for commands. We then decorate other methods with eg.

@main.command() to denote that these methods belong to the group main. This way we can add multiple commands to the CLI app.
We have covered the basics of creating CLI apps with Python. Click has much more to offer us. Next time we will extend buckets
with parameters, default values, flags and environment variables.

Share this article: Tweet this post / Post on LinkedIn