python

**6 Python Libraries That Transform Basic Scripts Into Professional CLI Tools**

Learn the 6 best Python libraries for building CLI tools — from argparse to Rich and Typer. Write cleaner, more powerful command-line tools with real code examples.

**6 Python Libraries That Transform Basic Scripts Into Professional CLI Tools**

I remember the first time I tried to build a command-line tool. I had a Python script that did something useful—renaming a bunch of files—but every time I wanted to change a setting I had to edit the script. That got old fast. I wanted to pass arguments from the terminal, show a nice help message, maybe add some color. I didn’t know where to start. Over the years I learned that Python has a whole ecosystem of libraries for exactly this purpose. They take you from a bare‑bones script to a polished tool that feels like it was written in C. Let me walk you through the six that I use most often. I will show you simple code, explain why each one exists, and share the little tricks I picked up along the way.


The first library you will encounter is argparse. It comes built into Python, so you don’t need to install anything. When I started, I thought it was complicated. It isn’t. It just wants you to tell it what arguments your tool expects. You create a parser object, add arguments, and call parse_args(). That’s it.

Let me show you a real example. Suppose you want to write a tool that reads a list of numbers from a file and prints their sum. You want the user to specify the input file and an optional flag to show verbose output.

import argparse

parser = argparse.ArgumentParser(description='Sum numbers from a file.')
parser.add_argument('filename', help='path to the input file')
parser.add_argument('--verbose', '-v', action='store_true', help='show details')
args = parser.parse_args()

with open(args.filename) as f:
    numbers = [float(line.strip()) for line in f if line.strip()]
total = sum(numbers)

if args.verbose:
    print(f'Found {len(numbers)} numbers. Sum: {total}')
else:
    print(total)

When you run python sum_tool.py data.txt -v, argparse turns the string data.txt into args.filename and sets args.verbose to True. It also gives you -h for free, which prints a help message. I love that argparse handles the boring stuff—type checking, error messages, default values—so I can focus on the logic.

A personal touch: I use argparse for every tool that I share with teammates. Nobody wants to remember the order of positional arguments. With argparse, you can define named flags like --output or --limit. The help message is always one command away. If you only learn one library for CLI tools, start here.


But argparse can feel verbose when your tool has many commands. That is where Click enters. Click uses decorators to turn functions into commands. You decorate a function with @click.command(), and then add @click.option() for each flag. The library handles the parsing and calls your function with the values.

Here is the same sum tool with Click:

import click

@click.command()
@click.argument('filename', type=click.Path(exists=True))
@click.option('--verbose', '-v', is_flag=True, help='show details')
def sum_tool(filename, verbose):
    with open(filename) as f:
        numbers = [float(line.strip()) for line in f if line.strip()]
    total = sum(numbers)
    if verbose:
        click.echo(f'Found {len(numbers)} numbers. Sum: {total}')
    else:
        click.echo(total)

if __name__ == '__main__':
    sum_tool()

Click automatically generates a nice help page, with the argument type (a path that must exist) and the flag. The click.echo function prints cleanly to the terminal. I like Click when my tool has several subcommands—like a git clone with commit, push, pull. You group commands using @click.group(). Click also supports callbacks, password prompts, and file arguments. It is mature and well‑documented.

One thing I often do with Click is use click.Choice to restrict an option to a set of values:

@click.option('--format', type=click.Choice(['json', 'csv', 'table']), default='json')

That alone prevents a whole class of user errors.


Then came Typer. Typer is built on top of Click, but it uses Python type hints to do most of the work. You write a plain function with type annotations, and Typer turns it into a CLI. No decorators needed for simple cases. It feels like magic the first time you try it.

Look at this:

import typer

def main(filename: str, verbose: bool = False):
    with open(filename) as f:
        numbers = [float(line.strip()) for line in f if line.strip()]
    total = sum(numbers)
    if verbose:
        typer.echo(f'Found {len(numbers)} numbers. Sum: {total}')
    else:
        typer.echo(total)

if __name__ == '__main__':
    typer.run(main)

Typer reads the function signature. The argument filename: str becomes a positional argument. The keyword argument verbose: bool = False becomes an optional flag --verbose or -v. It even generates a --help message that shows the types and defaults. If you run python sum_tool.py --help, you see something like:

Usage: sum_tool.py [OPTIONS] FILENAME

  Sum numbers from a file.

Arguments:
  FILENAME  [required]

Options:
  --verbose / --no-verbose  [default: False]
  --help                    Show this message and exit.

I use Typer when I want to turn an existing function into a CLI in ten seconds. It works especially well for small scripts that I build on the fly. If your function has Optional[str], List[int], or custom types, Typer handles those too. You can also use Click decorators inside Typer for advanced cases. Typer produces colored output automatically unless you turn it off.

One trick: if your function returns a string, Typer will print it. And you can use typer.Exit() to stop execution with a specific exit code. Perfect for error handling.


Now, the output. Most tutorials stop after parsing arguments, but real tools need to display results. That’s where Rich comes in. Rich transforms boring terminal output into something you’d expect from a professional application. It gives you tables, progress bars, colored text, markdown display, and even tree structures.

Let me show you a table. I built a tool that queries a database and shows results. Without Rich, I would print rows separated by commas. With Rich, I do this:

from rich.console import Console
from rich.table import Table

console = Console()
table = Table(title="Database Results")

table.add_column("ID", style="cyan", no_wrap=True)
table.add_column("Name", style="magenta")
table.add_column("Score", justify="right", style="green")

table.add_row("1", "Alice", "95")
table.add_row("2", "Bob", "87")
table.add_row("3", "Charlie", "92")

console.print(table)

The output is a neatly aligned table with colored columns. Rich respects your terminal width and wraps text if needed. You can also use Panel, Progress, Layout, and Syntax for highlighting code.

My favourite feature is the progress bar. When your tool downloads files or processes many items, show a progress bar so the user knows something is happening:

import time
from rich.progress import track

for i in track(range(100), description="Processing..."):
    time.sleep(0.02)

Rich works inside any CLI tool. I often combine it with Click or Typer. For example, inside a Click command, I create a Console object and use it to print formatted messages. This makes the tool feel polished without much effort.


Sometimes you need more than one‑shot commands. You want an interactive prompt where the user types commands, gets suggestions, and can navigate history. That’s Prompt Toolkit’s domain.

I wrote a little REPL for a custom query language. The user types select * from logs where level=error and gets results. Prompt Toolkit gives me:

  • Auto‑completion as the user types.
  • Syntax highlighting so keywords look different.
  • Multi‑line editing for long queries.
  • History that persists between sessions.

Here is a minimal example:

from prompt_toolkit import PromptSession
from prompt_toolkit.completion import WordCompleter
from prompt_toolkit.history import FileHistory

sql_keywords = ['select', 'from', 'where', 'insert', 'delete']
completer = WordCompleter(sql_keywords)

session = PromptSession(
    history=FileHistory('.query_history'),
    completer=completer,
    complete_while_typing=True
)

while True:
    try:
        text = session.prompt('sql> ')
        if text.strip().lower() == 'exit':
            break
        # process the query
        print(f'You said: {text}')
    except KeyboardInterrupt:
        continue
    except EOFError:
        break

When the user types se, the completer suggests select. Pressing Tab completes it. The history is saved to a file, so next time you run the tool, pressing up arrow brings back yesterday’s commands. You can also add custom key bindings, like Ctrl+R for reverse search.

I usually don’t need full REPL capabilities, but when I do, Prompt Toolkit saves me from reinventing the wheel. It is the same library that powers IPython’s terminal interface. If your tool acts as an interactive shell, this is your go‑to library.


Last but not least, Fire. Fire is the opposite of argparse. Instead of writing a parser, you pass any Python object to fire.Fire() and it becomes a CLI. It inspects the object’s attributes and methods and turns them into commands.

For example, I had a class that processes images:

import fire

class ImageProcessor:
    def resize(self, filename, width, height):
        print(f'Resize {filename} to {width}x{height}')
    def convert(self, filename, fmt):
        print(f'Convert {filename} to {fmt}')

if __name__ == '__main__':
    fire.Fire(ImageProcessor)

Now you can run python tool.py resize image.png 800 600 or python tool.py convert image.png jpg. Fire automatically generates help for each method. It also supports functions, dictionaries, and even modules.

I use Fire for quick prototypes. Suppose I have a script with several functions and I want to expose them all without writing any argument code. Fire does it. It also handles nested objects. If you pass a module, all its functions become subcommands.

The downside is that Fire’s help messages are not as pretty as Click’s, and it can get confused with complex types. But for throw‑away tools or internal utilities, Fire is unbeatable. It turns a class into a CLI in one line.


These six libraries cover every need I have ever had for command‑line tools. Start with argparse if you want the batteries included. Move to Click when you need subcommands and cleaner decorators. Use Typer when you want minimal code and type hints. Add Rich when you want beautiful output. Use Prompt Toolkit for interactive sessions. And grab Fire when you need something working in seconds.

I often combine them. For instance, a Typer command can use Rich inside to print a progress bar. A Click group can have a subcommand that opens an interactive REPL built with Prompt Toolkit. There is no rule that says you must pick only one.

Building a command‑line tool in Python is one of the most rewarding tasks. You create something that lives in the terminal—a constant companion for developers. With these libraries, you can make your tools clear, helpful, and even pleasant to use. Start small. Add one library at a time. You will be surprised how far a little code can go.

Keywords: Python CLI libraries, command-line tools Python, argparse tutorial, Click Python library, Typer Python CLI, Rich terminal output Python, Prompt Toolkit Python, Python Fire library, building CLI tools Python, Python argument parsing, Python terminal tools, command-line argument parsing Python, Python CLI development, argparse vs Click, Python Click decorators, Python type hints CLI, interactive Python CLI, Python REPL library, Python progress bar terminal, Rich library Python tables, Python subcommands CLI, Python CLI frameworks comparison, best Python CLI libraries, Python command-line parsing tutorial, Click vs Typer Python, Python terminal formatting, Python shell scripting libraries, Python CLI examples, argparse help message, Python Fire CLI, Python WordCompleter prompt, Python CLI for beginners, Python script arguments, Python developer tools, Click group commands Python, Typer type annotations CLI, Python terminal color output, Python FileHistory prompt toolkit, Python CLI tool design, Python click.Choice options, Python CLI automation



Similar Posts
Blog Image
How Can FastAPI Make Your Web Apps Handle Requests Like a Pro Juggler?

Boost Your Web App's Efficiency and Speed with Asynchronous Programming in FastAPI

Blog Image
**Python Libraries That Accelerate Scientific Computing: NumPy, SciPy, Pandas and Dask Performance Guide**

Discover Python's powerful scientific computing libraries: NumPy, SciPy, Pandas & more. Learn efficient data analysis, visualization & machine learning tools. Master scientific Python today!

Blog Image
5 Essential Python Libraries for Web Development: Expert Insights

Explore 5 powerful Python libraries for web development. From Django's robustness to Flask's simplicity, discover the right tool for your next project. Learn how to build efficient, scalable web applications.

Blog Image
Is Web Scraping the Ultimate Superpower Hidden in Your Browser?

Unlocking Web Data with Python: The Adventures of Beautiful Soup and Selenium

Blog Image
How Can OAuth2 and FastAPI Make Your API as Exclusive as a VIP Club?

Guarding Your API Like a VIP Club with OAuth2 and FastAPI

Blog Image
Python's Secrets: Customizing and Overloading Operators with Python's __op__ Methods

Python's magic methods allow customizing operator behavior in classes. They enable addition, comparison, and exotic operations like matrix multiplication. These methods make objects behave like built-in types, enhancing flexibility and expressiveness in Python programming.