Adventures in Machine Learning

Building CLI Applications with Python: An Introduction

Command-line applications (CLI) have been around since the early days of computing. They are powerful tools for executing complex tasks and tasks that need to be done repeatedly without user intervention.

CLI tools are essential for developers and system administrators as they provide efficient ways to interact with computer systems. Thanks to some excellent tools like argparse, click, and docopt, developing CLI applications has never been easier.

In this article, we’ll get into the basics of CLI application development and learn how to build our first CLI applications.

Running basic commands:

The first step in building any CLI application is to create and run basic commands.

We can use any programming language to build CLI applications, but for simplicity and ease of use, we’ll use Python in this article. Python is a popular programming language that has excellent libraries for building CLI applications.

To run a basic command, we’ll create a new Python file and add the following code:

#!/usr/bin/env python
print("Hello, World!")

Save the file as hello.py. Open the terminal, navigate to the directory of the hello.py file, and type ./hello.py.

You’ll see the output “Hello, World!” printed in the terminal.

Adding command-line arguments:

Often, we need to pass arguments to our CLI applications.

To do this, we need to use Python’s sys.argv list, which contains the command-line arguments passed to our application. Let’s modify our hello.py file to accept a name argument.

#!/usr/bin/env python

import sys
name = sys.argv[1]
print(f"Hello, {name}!")

Save the file as hello.py. Open the terminal, navigate to the directory of the hello.py file, and type ./hello.py John.

You’ll see the output “Hello, John!” printed in the terminal.

Adding options/flags:

Options and flags are additional arguments that can be passed to our CLI applications.

Options are arguments that require a value, while flags are arguments that toggle a Boolean value. We can use libraries like argparse, click, and docopt to make it easier to add these options and flags to our application.

Let’s modify our hello.py file to accept an optional --upper flag.

Using argparse:

#!/usr/bin/env python

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("name", help="The name of the person.")
parser.add_argument("--upper", help="Convert name to uppercase.", action="store_true")
args = parser.parse_args()
if args.upper:
    name = args.name.upper()
else:
    name = args.name
print(f"Hello, {name}!")

Using click:

#!/usr/bin/env python

import click
@click.command()
@click.argument("name")
@click.option("--upper", is_flag=True, help="Convert name to uppercase.")
def hello(name, upper):
    if upper:
        name = name.upper()
    click.echo(f"Hello, {name}!")
if __name__ == "__main__":
    hello()

Using docopt:

#!/usr/bin/env python
"""Usage: hello.py [options] NAME
Options:
    -u, --upper      Convert name to uppercase.
"""

from docopt import docopt
args = docopt(__doc__)
name = args["NAME"]
if args["--upper"]:
    name = name.upper()
print(f"Hello, {name}!")

All three libraries function similarly. They create a parser, add arguments, parse the arguments, and run the appropriate functions based on the arguments passed.

Conclusion:

In conclusion, CLI applications are an essential tool for developers and system administrators. They provide an efficient way of interacting with computer systems.

Python is a popular language that makes it easy to build these applications, thanks to libraries like argparse, click, and docopt. We learned how to create and run basic CLI commands and how to add command-line arguments, options, and flags.

In the end, we saw how to use argparse, click, and docopt libraries to add these features. Knowing how to build CLI applications is an essential skill that every developer must have.

Task-runner libraries and command-line interfaces:

CLI applications can be used for more than just running simple commands. They can also be used to automate complex tasks with ease.

A task-runner library like Invoke can be used to create and run tasks from the command line. Invoke is a Python library that provides a simple way to define and run tasks.

It lets you write tasks in a familiar Python syntax and provides a command-line interface for running them. To use Invoke, you first need to install it using pip.

pip install invoke

Once installed, you can define your tasks using Python functions. Here is an example:

from invoke import task
@task
def build():
    print("Building app...")
@task
def deploy():
    print("Deploying app...")

To run these tasks, open the terminal, navigate to the directory containing the tasks file, and type invoke build or invoke deploy. These commands will execute the corresponding tasks defined in the Python code.

Packaging command-line applications:

Once you have written your CLI application, the next step is to package it. Packaging your application makes it easy to distribute and install on other machines.

Python provides built-in tools like setuptools and distutils for packaging and distributing Python modules. To distribute a CLI application, you need to define an entry point in your setup.py file.

Here’s an example setup.py file:

from setuptools import setup, find_packages

setup(
    name="example",
    version="0.1",
    packages=find_packages(),
    entry_points={
        'console_scripts': ['app=example.app:main']
    }
)

This setup.py file defines an app called “example,” and its entry point is set to “example.app:main.” This means that the command “app” will run the “main” function defined in the “example.app” module. To create a distributable package, run the following command:

python setup.py sdist

This command creates a source distribution file in the dist directory.

To install the package, use the following command:

pip install dist/example-0.1.tar.gz

This command installs the package, and the entry point is now available on the command line.

Commands:

Overview of command implementation for each library:

All three libraries, argparse, docopt, and click, provide a way to define commands in Python syntax.

The implementation of commands varies slightly between the libraries.

Argparse command implementation:

Argparse provides a way to define subcommands using add_subparsers method.

Here is an example:

import argparse
def foo(args):
    print("Foo!")
def bar(args):
    print("Bar!")
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
parser_foo = subparsers.add_parser("foo")
parser_foo.set_defaults(func=foo)
parser_bar = subparsers.add_parser("bar")
parser_bar.set_defaults(func=bar)
args = parser.parse_args()
args.func(args)

This code defines two subcommands, “foo” and “bar,” each with its own function. The set_defaults method sets the function that will be run when the subcommand is called.

Docopt command implementation:

Docopt uses the docstring of a Python file to define commands. Here is an example:

"""
Program usage.

Usage:
  program foo
  program bar
Options:
  -h --help     Show this screen.
"""
def main(args):
    if args["foo"]:
        print("Foo!")

    if args["bar"]:
        print("Bar!")
if __name__ == "__main__":

    from docopt import docopt
    main(docopt(__doc__))

In this code, we define two subcommands, “foo” and “bar,” within the program usage docstring using the syntax program foo and program bar.

The docopt(__doc__) method parses the docstring options and returns a dictionary containing the arguments.

Click command implementation:

Click uses a decorator syntax to define commands.

Here is an example:

import click
@click.group()
def cli():
    pass
@cli.command()
def foo():
    print("Foo!")
@cli.command()
def bar():
    print("Bar!")
if __name__ == "__main__":
    cli()

The @click.group() decorator creates a command group. Commands are then created using the @cli.command() decorator.

When the CLI application is run, the if __name__ == "__main__": cli() line runs the CLI application.

Conclusion:

In this article, we have learned about task-runner libraries and how to use Invoke to create and run tasks from the command line.

We have also learned about packaging CLI applications using Python’s built-in tools like setuptools and distutils. Lastly, we have discussed the implementation of commands in argparse, docopt, and click, and how to define them in Python syntax.

With this knowledge, you can build powerful CLI applications that automate complex tasks, are easy to distribute and install, and provide a fantastic user experience.

Arguments:

Adding positional arguments:

Positional arguments are arguments that are passed to a CLI application without a flag or option.

Adding positional arguments in argparse is straightforward using the add_argument method. Here is an example:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("name", help="The name of the person.")
parser.add_argument("age", type=int, help="The age of the person.")
args = parser.parse_args()
print(f"Name: {args.name}")
print(f"Age: {args.age}")

This code defines two positional arguments, “name” and “age,” with their respective help messages. The order in which these arguments are passed on the CLI is significant, and they will be parsed in the same order and passed to args as attributes.

In Click, positional arguments are added as function arguments using the @click.argument decorator. Here is an example:

import click
@click.command()
@click.argument("name")
@click.argument("age", type=int)
def example(name, age):
    click.echo(f"Name: {name}")
    click.echo(f"Age: {age}")
if __name__ == "__main__":
    example()

This code defines two positional arguments, “name” and “age,” as function arguments. The type keyword argument specifies the type conversion for the age argument.

Accessing arguments in function logic:

Once we have defined positional arguments, we can access them in the function logic using the args namespace in argparse. Here is an example:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("name", help="The name of the person.")
parser.add_argument("age", type=int, help="The age of the person.")
args = parser.parse_args()
if args.age < 18:
    print(f"{args.name} is a minor.")
else:
    print(f"{args.name} is an adult.")

In this example, we access the “name” and “age” arguments in the function logic using the args namespace and perform a simple comparison to check if the person is a minor or an adult.

Flags/Options:

Adding options and flags with argparse:

Options and flags are additional arguments that can be passed to our CLI applications.

We add them using the add_argument function in argparse. Here is an example:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--verbose", help="Increase output verbosity", action="store_true")
args = parser.parse_args()
if args.verbose:
    print("Verbose mode on.")

In this example, we use the --verbose option and set the action to “store_true” to store the Boolean value True when the option is passed.

Overcoming limits of docopt for multiple defaults:

Docopt has limitations when it comes to multiple defaults for an option.

For example, an option with multiple default values will only be set to the first value. To overcome this limitation, we can use multiple options with the same name.

Here is an example:

"""
Program usage.
Usage:
  program [options]
Options:
  -o --option=  Set option [default: value1]
  --option=     Set option [default: value2]
"""
args = docopt(__doc__)
if args["--option"]:
    options = args["--option"]
else:
    options = ["value1", "value2"]
print(f"Options: {options}")

In this code, we define two options with the same name, but different default values.

If the user passes an option, that value will be used. If not, the default values will be used.

Adding options and flags with Click:

Click provides the @click.option decorator to add options and flags to our CLI application. Here is an example:

import click
@click.command()
@click.option("--verbose", is_flag=True, help="Increase output verbosity")
def example(verbose):
    if verbose:
        click.echo("Verbose mode on.")
if __name__ == "__main__":
    example()

In this code, we use the @click.option decorator to add the “verbose” flag to our CLI application. The is_flag=True option specifies that this is a Boolean flag.

Conclusion:

In this article, we have learned about the different types of arguments that we can pass to our CLI applications, including positional arguments and options/flags. We have seen how to add these types of arguments using argparse, docopt, and Click.

We have also discussed how to access these arguments in the function logic of our CLI applications. With this knowledge, we can build powerful and flexible CLI applications that can handle a wide range of inputs and options.

Version Option:

Adding a --version argument to show the version number is a common requirement for CLI applications. It provides users with the essential information about the version of the software they’re using.

In this section, we’ll discuss how to add a --version argument to CLI applications using argparse, docopt, and Click.

Implementation with argparse:

Argparse provides a built-in way to add a --version argument using the add_argument function.

The add_argument function has an action parameter with a version option that sets the version number. Here is an example:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--version', action='version', version='1.0')
args = parser.parse_args()
if args.version:
    print(args.version)

In this example, we add a --version argument to the parser and set the version number as 1.0. The action='version' part of the code sets the action of the argument to version, which prints the version number and exits the program.

Implementation with docopt:

In docopt, we can define the --version argument as a special option. Here’s an example:

"""
Program usage.

Usage:
  program [options]
Options:
  --version     Show version number.
"""
VERSION = "1.0"
args = docopt(__doc__, version=VERSION, options_first=True)
if args["--version"]:
    print(f"Version {VERSION}")

In this example, we define the --version argument in the docstring using the same syntax we use for other options.

The version keyword argument sets the version number to 1.0. The options_first=True keyword argument in the docopt method tells docopt to stop parsing options once it finds a non-option argument.

In the last line, we check if the --version option was passed and print the version number.

Implementation with Click:

In Click, we can use the @click.version_option decorator to add the --version option.

Here’s an example:

import click
@click.command()
@click.version_option(version='1.0')
def example():
    pass
if __name__ == "__main__":
    example()

In this example, we add the @click.version_option decorator to the CLI function. The version keyword argument sets the version number to 1.0.

Popular Posts