Writing a “simple” application is rarely a simple task. Even simple code for a simple task requires interactions with the quirks of your programming language, exception handling, input sanitizing, intuitive design, and much more. This is why I appreciate a library that makes development simple. One of my favorite Python libraries is Click, a module that handles every aspect of command-line options. In this article, I demonstrate how to use it.
For this tutorial, I reuse the source code of my open source rpm_query application. The rpm_query application is a collection of simple commands that can query your system’s RPM database from a terminal.
Requirements
To follow along with this tutorial, you must have:
- Linux distribution, preferably one that uses RPM (Like Fedora or Red Hat Enterprise Linux)
- Python 3.7 or greater
- Git
- A familiarity with virtual environments
- An Internet connection so you can download dependencies
A common CLI
This script uses a module inside the reporter package to query the RPM database:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
#!/usr/bin/env python """ # rpmq_simple.py - A simple CLI to query the sizes of RPM on your system Author: Jose Vicente Nunez """ import argparse import textwrap from reporter import __is_valid_limit__ from reporter.rpm_query import QueryHelper if __name__ == "__main__": parser = argparse.ArgumentParser(description=textwrap.dedent(__doc__)) parser.add_argument( "--limit", type=__is_valid_limit__, # Custom limit validator action="store", default=QueryHelper.MAX_NUMBER_OF_RESULTS, help="By default results are unlimited but you can cap the results" ) parser.add_argument( "--name", type=str, action="store", help="You can filter by a package name." ) parser.add_argument( "--sort", action="store_false", help="Sorted results are enabled bu default, but you fan turn it off" ) args = parser.parse_args() with QueryHelper( name=args.name, limit=args.limit, sorted_val=args.sort ) as rpm_query: for package in rpm_query: print(f"{package['name']}-{package['version']}: {package['size']:,.0f}") |
Install it in editable mode:
1 2 3 4 |
$ git clone XXXX CLIWithCLickAndTrogon $ python3 -m venv ~/virtualenv/CLIWithCLickAndTrogon $ . ~/virtualenv/CLIWithCLickAndTrogon/bin/activate $ python3 -m pip install --editable . |
See it in action:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
(env)$ rpmq_simple.py --help usage: rpmq_simple.py [-h] [--limit LIMIT] [--name NAME] [--sort] # rpmq_simple.py - A simple CLI to query the sizes of RPM on your system Author: Jose Vicente Nunez options: -h, --help show this help message and exit --limit LIMIT By default results are unlimited but you can cap the results --name NAME You can filter by a package name. --sort Sorted results are enabled bu default, but you fan turn it off (env)$ rpmq_simple.py --name kernel --limit 5 kernel-6.2.11: 0 kernel-6.2.14: 0 kernel-6.2.15: 0 |
1 |
Most of the code on the <a href="https://github.com/josevnz/CLIWithClickAndTrogon/blob/main/scripts/rpmq_simple.py">rpmq_simple.py</a> script is boilerplate for the command line interface, using the standard <a href="https://docs.python.org/3/library/argparse.html">ArgParse</a> library. ArgParse is powerful, but it can be intimidating at first. |
Process command-line arguments with Click
The Click framework makes it easy to parse command-line arguments. To prove that, convert my rpm_query command to Click:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#!/usr/bin/env python """ # rpmq_click.py - A simple CLI to query the sizes of RPM on your system Author: Jose Vicente Nunez """ import click from reporter.rpm_query import QueryHelper @click.command() @click.option('--limit', default=QueryHelper.MAX_NUMBER_OF_RESULTS, help="By default results are unlimited but you can cap the results") @click.option('--name', help="You can filter by a package name.") @click.option('--sort', default=True, help="Sorted results are enabled bu default, but you fan turn it off") def command( name: str, limit: int, sort: bool ) -> None: with QueryHelper( name=name, limit=limit, sorted_val=sort ) as rpm_query: for package in rpm_query: click.echo(f"{package['name']}-{package['version']}: {package['size']:,.0f}") if __name__ == "__main__": command() |
There are two big changes:
- Most of the boilerplate code from ArgParse is done, replaced by annotations.
- The Click library adds decorators to a new function called
command
that takes arguments and executes the RPM query.
Run the new script to confirm that it works exactly as before.
Using setuptools and Click
The Click documentation mentions that you should use setuptools to automatically create a wrapper for your tool. The documentation uses deprecated syntax for setup.py
, but you can use the new setup.cfg
format instead, creating a package called scripts inside the package called reporter.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
[metadata] name = CLIWithClick version = 0.0.1 author = Jose Vicente Nunez Zuleta author-email = kodegeek.com@example.com license = Apache 2.0 summary = Query your RPM database home-page = https://github.com/josevnz/cliwithclickandtrogon description = Query your RPM database. A tutorial. long_description = file: README.md long_description_content_type = text/markdown [options] packages = reporter setup_requires = setuptools wheel build pip twine install_requires = importlib; python_version == "3.9" click scripts = scripts/rpmq_simple.py scripts/rpmq_click.py [options.entry_points] console_scripts = rpmq = reporter.scripts:command |
A script called rpmq is created by setuptools, and it behaves exactly as the previous script, but no boilerplate code to pass arguments to Click is needed.
Next steps
- Download the source code for this tutorial and start experimenting.
- Click has great documentation and online support. Take advantage of it!
- Click has much more complex examples, so take a look at its gallery.
0 Comments