Building Command Line Apps in Python with Click

Have you ever wondered how you can easily create command-line applications in Python? Gather yourself up because that is what I am going to cover today.



Building Command Line Apps in Python with Click
Image by Author | DALLE-3 & Canva

 

Sometimes, what seems very complex can often be made very simple and that's exactly what the library 'Click' achieves. It makes creating command-line applications in Python easy and straightforward. For example, you can use click to build a file organizer that sorts files into folders based on their type. Developers use click to automate their everyday tasks without getting stuck in the complexity of syntax and procedures. Not only that, but this library also allows integrations with other Python libraries, so that you can enhance your applications even further. Ultimately, I can say that Click makes the developer's life much easier.

 

Why Click?

 

Click is a great choice for building command-line apps in Python because it offers many useful features that other utilities may lack.

  • Easy to combine commands: Click allows lazy composition of commands without restrictions.
  • Follows standard conventions: Supports Unix/POSIX command-line conventions.
  • Environment variable support: Can load values directly from environment variables.
  • Useful helpers: Provides common helpers such as fetching direct keyboard input, screen clearing, getting terminal dimensions, and finding configuration paths.
  • Custom value prompting: Easily prompts users for input when required.
  • File handling: Built-in support for handling files.
  • Extensibility: You can easily create custom commands and integrate Click into larger applications, enhancing adaptability.

 

Getting Started

 

First, you need to install the library by using the following command:

pip install click

 

Click offers many advanced features, but let’s focus on the fundamental concepts to give you a solid understanding of the library and help you create CLI apps effectively.

 

1. Commands

@click.command() is a decorator in Click that defines a function into a CLI command, making it executable from the command line. Let’s understand by creating a simple app that prints a farewell message:

import click

@click.command()
def farewell():
    """ Simple program that prints a farewell message. """
    click.echo('Goodbye! Take care.')

if __name__ == '__main__':
    farewell()

 

click.echo() is a utility function that prints output to the terminal.

You can run the app from your terminal as:

python3 farewell.py

 

Output:

Goodbye! Take care.

 

2. Options

@click.option() is used to add command-line options to commands in Click. These options are optional parameters or flags you can pass to a command to modify its behavior. They typically start with a double dash (--). You can enforce data types for these options (e.g., int, float, str), set default values, prompt users for input if the option is not provided, and include help text, which will be shown when users invoke the --help flag. This makes commands more flexible and user-friendly.

Now, that you know these basics it will be easier for you to follow the example that calculates the area of the rectangle:

import click

@click.command()
@click.option('--length', type=float, default=3, prompt='Length of the rectangle')
@click.option('--width', type=float, default=2, prompt='Width of the rectangle')
def area(length, width):
    """ Calculate the area of a rectangle. """
    if length <= 0 or width <= 0:
        click.echo("Length and width must be positive values.")
    else:
        area = length * width
        click.echo(f'The area of the rectangle with length {length} and width {width} is {area}')

if __name__ == '__main__':
    area()

 

In this example,

  • @click.command() defines the command area which calculates the area of the rectangle.
  • @click.option() takes length and width as input from the user and ensures it's of type float. Notice that the type is string by default and you have to specify otherwise. The default values of 3 for length and 2 for width are used if the user does not provide these values through command-line flags and also skips them during the prompting i.e. pressing Enter without providing the values.
  • The formula length * width is used to compute the area.
  • The program checks if the length or width value is negative and displays an error message if needed.

Run the App

  • You can run this app from your terminal as follows:
  • python3 rectangle_area.py

     

    You will be prompted to enter the length value. In my case, I have given the value as 4.

    Length of the rectangle:4

     

    Give the value and press Enter.

    Now, you will be prompted to enter the width value. I have given the width value as 11.

    Width of the rectangle:11

     

    Press Enter after it.

  • Or you can directly provide the values of length and width and run it as follows:
  • python3 rectangle_area.py --length 4 --width 11

     

Output

The area of the rectangle with length 4.0 and width 11.0 is 44.0

 

3. Multi-Valued Options

Multi-valued options in Click allow you to pass multiple values to a single option. For this purpose, set the multiples= True parameter, which is False by default. Let's understand this concept by calculating the area of a rectangle using multiple values:

import click

@click.command()
@click.option('--length', multiple=True, type=float)
@click.option('--width', multiple=True, type=float)
def area(length, width):
    """ Calculate the area of multiple rectangles. """
    if len(length) != len(width):
        click.echo("The number of lengths must match the number of widths.")
        return

    for l, w in zip(length, width):
        if l <= 0 or w <= 0:
            click.echo(f"Length {l} and width {w} must be positive values.")
        else:
            area = l * w
            click.echo(f'The area of the rectangle with length {l} and width {w} is {area}')

if __name__ == '__main__':
    area()

 

You can run this app from your terminal as follows:

python3 rectangle_area.py --length 2 –-length 3 --width 3 --width 6

 

Output

The area of the rectangle with length 2.0 and width 3.0 is 6.0
The area of the rectangle with length 3.0 and width 6.0 is 18.0

 

4. Arguments

In Click, arguments are positional parameters you must provide in the order specified by the command. Unlike options, which are specified using flags (like --name), arguments are required and do not use double dashes (--). Moreover, you cannot set default values for arguments or prompt the user for them; they must be provided directly when the command is run.

import click

@click.command()
@click.argument('length', type=float)
@click.argument('width', type=float)
def area(length, width):
    """ Calculate the area of a rectangle. """
    if length <= 0 or width <= 0:
        click.echo("Length and width must be positive values.")
    else:
        area = length * width
        click.echo(f'The area of the rectangle with length {length} and width {width} is {area}')

if __name__ == '__main__':
    area()

 

To run this app, you provide the length and width arguments directly in the command line:

python3 rectangle_area.py 5 10

 

Output

The area of the rectangle with length 5.0 and width 10.0 is 50.0

 

5. Grouping Commands Together

In Click, you can group related commands using @click.group(). This creates a CLI app with multiple subcommands, making it easier to manage and organize various functions under one command group. Let’s explore this with the help of an example:

import click

@click.group()
def rectangle():
    """ Commands for rectangle calculations. """
    pass

@click.command()
@click.option('--length', prompt='Length of the rectangle', type=float)
@click.option('--width', prompt='Width of the rectangle', type=float)
def area(length, width):
    """ Calculate the area of a rectangle. """
    if length <= 0 or width <= 0:
        click.echo("Length and width must be positive values.")
    else:
        area = length * width
        click.echo(f'The area of the rectangle with length {length} and width {width} is {area}')

@click.command()
@click.option('--length', prompt='Length of the rectangle', type=float)
@click.option('--width', prompt='Width of the rectangle', type=float)
def perimeter(length, width):
    """ Calculate the perimeter of a rectangle. """
    if length <= 0 or width <= 0:
        click.echo("Length and width must be positive values.")
    else:
        perimeter = 2 * (length + width)
        click.echo(f'The perimeter of the rectangle with length {length} and width {width} is {perimeter}')

# Register the commands with the group
rectangle.add_command(area)
rectangle.add_command(perimeter)

if __name__ == '__main__':
    rectangle()

 

In this example,

  • @click.group() creates a command group named rectangle to organize related subcommands.
  • @click.command() defines individual subcommands like area and perimeter.
  • @click.option('--length') and @click.option(‘--width’) prompt the user for the length and width values, enforcing type and input.
  • rectangle.add_command(area) and rectangle.add_command(perimeter) attach these subcommands to the rectangle group.

When you run the CLI, you will use the circle command, followed by a subcommand (area or perimeter).

To calculate the area, run the following command:

python3 rectangle_calc.py area --length 2 --width 9

 

Output

The area of the rectangle with length 2.0 and width 9.0 is 18.0

 

To calculate the perimeter:

python3 rectangle_calc.py perimeter --length 2 --width 9

 

Output

The perimeter of the rectangle with length 2.0 and width 9.0 is 22.0

 

Documenting Commands, Options & Arguments

Documenting arguments, commands, and options is essential because it ensures that users can effectively interact with the app and quickly understand its functionalities.

The help parameter in the @click.option() decorator describes the command-line option. On the other hand, commands and arguments are documented with the help of the docs string. Let’s understand it with the help of an example:

import click
@click.command()
@click.option('--radius', type=float, default=5.0, help='Radius of the circle.')
@click.argument('color')
def describe_circle(radius, color):
    """
    Describes a circle with a given radius and color.

    Arguments:
    color: The color of the circle.

    """
    click.echo(f'The circle has a radius of {radius} and is colored {color}.')

if __name__ == '__main__':
    describe_circle()

 

Now, open your terminal and invoke the help flag as:

python3 circle.py --help

 

The output will be:

Usage: circle.py [OPTIONS] COLOR

Describes a circle with a given radius and color.

Arguments:
  color TEXT  The color of the circle.

Options:
  --radius FLOAT  The radius of the circle. Defaults to 5.0.
  --help           Show this help message and exit.

 

This clarifies the command's description, the required arguments, and the available options. So, be sure to include thorough documentation along with good functionality in your CLI app.

 

Wrapping Up

 
In this guide, we've explored the essential concepts needed to build command-line applications with Click. I hope these explanations have clarified the basics for you. For more advanced concepts and detailed usage, I recommend checking out the Click documentation.
 
 

Kanwal Mehreen Kanwal is a machine learning engineer and a technical writer with a profound passion for data science and the intersection of AI with medicine. She co-authored the ebook "Maximizing Productivity with ChatGPT". As a Google Generation Scholar 2022 for APAC, she champions diversity and academic excellence. She's also recognized as a Teradata Diversity in Tech Scholar, Mitacs Globalink Research Scholar, and Harvard WeCode Scholar. Kanwal is an ardent advocate for change, having founded FEMCodes to empower women in STEM fields.





Our Top 3 Partner Recommendations



1. Best VPN for Engineers - 3 Months Free - Stay secure online with a free trial

2. Best Project Management Tool for Tech Teams - Boost team efficiency today

4. Best Password Management Tool for Tech Teams - zero-trust and zero-knowledge security