Courses/Python/Building Command-Line Tools with argparse & Typer

    Lesson 35 โ€ข Advanced

    Building Command-Line Tools with argparse & Typer

    Build real command-line tools the same way professionals build developer utilities, automated scripts, data pipelines, AI model runners, and deployment tools. Master both argparse (standard, built-in) and Typer (modern, FastAPI-style) for creating production-grade CLI applications.

    ๐Ÿงฐ What You'll Learn

    This lesson teaches how to build real command-line tools, the same way professionals build:

    โœ” Developer utilities

    โœ” Automated scripts

    โœ” Data pipelines

    โœ” AI model runners

    โœ” Server management tools

    โœ” Deployment scripts

    โœ” Productivity tools

    You will learn two approaches:

    1. argparse (standard, low-level, built into Python) โ€” Perfect for: small scripts, simple tools, full control.

    2. Typer (modern, high-level, super-fast development) โ€” Perfect for: professional apps, developer tools, banners, sub-commands, autocomplete.

    Part 1: CLI Fundamentals โ€” argparse Basics & Introduction to Typer

    ๐Ÿ”ฅ 1. Why Command-Line Tools Matter

    Before GUIs, everything ran in the terminal. But even today:

    • developers use CLI tools daily (git, pip, docker)
    • servers run scripts using CLI arguments
    • automations depend on commands and flags
    • real engineers build internal CLI tools to improve workflows
    • cloud deployment (AWS/GCP/Azure) relies heavily on CLI automation
    CLI ToolWhat It DoesWhy It's Fast
    git pushUpload code to server1 command vs. clicking through UI
    pip installAdd a libraryInstant, no browser needed
    docker runStart a containerScriptable, automatable
    python script.py --helpShow tool optionsSelf-documenting

    Having CLI-building skills makes you 10ร— more valuable as a developer.

    โš™๏ธ 2. argparse Basics โ€” The Standard Python CLI Option

    argparse ships with Python. No installation needed.

    argparse Basics

    Basic CLI tool with argparse

    Try it Yourself ยป
    Python
    import argparse
    
    parser = argparse.ArgumentParser(description="Simple greeting tool")
    parser.add_argument("name", help="The name of the person to greet")
    
    # For demo purposes, we'll parse known args
    # In real CLI: args = parser.parse_args()
    args = parser.parse_args(["Boopie"])
    
    print(f"Hello, {args.name}!")
    
    # Run in terminal: python app.py Boopie
    # Output: Hello, Boopie!

    ๐Ÿง  3. Positional vs Optional Arguments

    TypeSyntaxExampleRequired?
    Positionalfilenamepython tool.py data.csvYes
    Optional--verbose or -vpython tool.py --verboseNo

    Positional โ€” Required inputs:

    Positional Arguments

    Required input arguments

    Try it Yourself ยป
    Python
    import argparse
    
    parser = argparse.ArgumentParser()
    parser.add_argument("path", help="File path")
    
    # Demo: parse with sample argument
    args = parser.parse_args(["myfile.txt"])
    print(f"Path: {args.path}")

    Optional โ€” Flags that start with - or --:

    Optional Arguments

    Flags with - or --

    Try it Yourself ยป
    Python
    import argparse
    
    parser = argparse.ArgumentParser()
    parser.add_argument("--verbose", "-v", action="store_true")
    
    # Demo: parse with verbose flag
    args = parser.parse_args(["--verbose"])
    print(f"Verbose mode: {args.verbose}")

    Usage: python tool.py file.txt --verbose

    Optional flags improve usability and mirror real CLI tools like pip, git, and docker.

    ๐Ÿ“ 4. Typed Arguments: int, float, file paths

    argparse automatically converts types:

    Typed Arguments

    Automatic type conversion

    Try it Yourself ยป
    Python
    import argparse
    
    parser = argparse.ArgumentParser()
    parser.add_argument("--count", type=int, default=1)
    parser.add_argument("--rate", type=float, default=1.0)
    
    # Demo: parse with typed arguments
    args = parser.parse_args(["--count", "5", "--rate", "2.5"])
    print(f"Count: {args.count}, Rate: {args.rate}")
    
    # Or accept file objects:
    # parser.add_argument("--file", type=argparse.FileType("r"))

    This is extremely useful for real tools.

    ๐Ÿงฉ 5. Sub-Commands (Like Git)

    Many tools behave like: git add, git push, git commit. You can create the same pattern:

    Sub-Commands

    Git-style subcommands

    Try it Yourself ยป
    Python
    import argparse
    
    parser = argparse.ArgumentParser()
    sub = parser.add_subparsers(dest="command")
    
    add_cmd = sub.add_parser("add")
    add_cmd.add_argument("numbers", nargs="+", type=int)
    
    show_cmd = sub.add_parser("show")
    
    # Demo: parse "add" subcommand
    args = parser.parse_args(["add", "1", "2", "3"])
    if args.command == "add":
        print(f"Sum: {sum(args.numbers)}")

    Example usage:

    python tool.py add 1 2 3
    python tool.py show

    This transforms your script into a multi-command CLI application.

    โšก 6. Real Project Example โ€” File Organizer

    File Organizer CLI

    Real project example

    Try it Yourself ยป
    Python
    import argparse
    import os
    import shutil
    
    def main():
        parser = argparse.ArgumentParser(description="Organize files by extension")
        parser.add_argument("folder")
    
        # Demo: simulate with current directory info
        print("File Organizer Tool")
        print("Usage: python organize.py <folder>")
        print("This tool organizes files into subfolders by extension.")
        
        # Example of what the tool would do:
        sample_files = ["report.pdf", "image.jpg", "script.py"]
        for file in sample_file
    ...

    Run: python organize.py downloads/

    Automatically sorts files into subfolders.

    ๐Ÿš€ 7. Introduction to Typer (The Modern CLI Framework)

    Typer is built by the creator of FastAPI. It offers:

    FeatureargparseTyper
    Setup Code10+ lines2-3 lines
    Type ValidationManualAutomatic
    Help GenerationBasicBeautiful
    Colors/FormattingManualBuilt-in
    AutocompletionComplexOne command

    Install: pip install typer[all]

    ๐Ÿง  8. Basic Typer Example

    Basic Typer Example

    Modern CLI framework

    Try it Yourself ยป
    Python
    # Note: Typer requires installation: pip install typer[all]
    # This is a conceptual example
    
    # import typer
    # app = typer.Typer()
    
    # @app.command()
    # def greet(name: str):
    #     typer.echo(f"Hello {name}!")
    
    # if __name__ == "__main__":
    #     app()
    
    # Simulated output:
    print("Typer CLI Example")
    print("Command: greet Boopie")
    print("Output: Hello Boopie!")
    print()
    print("Typer generates:")
    print("  โœ” automatic help screen")
    print("  โœ” validation")
    print("  โœ” color output")
    print("  โœ” autocomplete
    ...

    Run: python app.py greet Boopie

    ๐ŸŽ› 9. Optional Arguments in Typer

    It's cleaner than argparse:

    Optional Arguments in Typer

    Cleaner than argparse

    Try it Yourself ยป
    Python
    # Typer optional arguments example
    # @app.command()
    # def download(url: str, retries: int = typer.Option(3), verbose: bool = False):
    #     typer.echo(f"Downloading {url} with {retries} retries, verbose={verbose}")
    
    # Simulated output:
    url = "https://example.com"
    retries = 5
    verbose = True
    print(f"Downloading {url} with {retries} retries, verbose={verbose}")
    
    # Usage: python tool.py download https://example.com --retries 5 --verbose

    Usage: python tool.py download https://example.com --retries 5 --verbose

    ๐Ÿงฉ 10. Typed Options & Defaults

    Typed Options

    Automatic type validation

    Try it Yourself ยป
    Python
    # Typer typed options example
    # @app.command()
    # def math(x: int, y: int = 10):
    #     typer.echo(x + y)
    
    # Simulated:
    x = 5
    y = 10
    print(f"x + y = {x + y}")
    
    # Typer automatically:
    # โœ” validates integers
    # โœ” uses type hints
    # โœ” generates help text

    This makes code cleaner and more maintainable.

    ๐Ÿ— 11. Subcommands (Typer's strongest feature)

    Typer Subcommands

    Multi-command CLI apps

    Try it Yourself ยป
    Python
    # Typer subcommands example
    # import typer
    
    # app = typer.Typer()
    # files = typer.Typer()
    # users = typer.Typer()
    
    # app.add_typer(files, name="files")
    # app.add_typer(users, name="users")
    
    # @files.command()
    # def clean():
    #     typer.echo("Cleaning files...")
    
    # @users.command()
    # def add(name: str):
    #     typer.echo(f"Added user: {name}")
    
    # Simulated output:
    print("Command: python tool.py files clean")
    print("Output: Cleaning files...")
    print()
    print("Command: python tool.py users add Boopie
    ...

    Run:

    python tool.py files clean
    python tool.py users add Boopie

    You now have a full CLI program with modules.

    ๐Ÿง  12. Real Project Example โ€” AI Assistant CLI

    AI Assistant CLI

    Real project example

    Try it Yourself ยป
    Python
    # AI Assistant CLI example
    # import typer
    # import openai
    
    # app = typer.Typer()
    
    # @app.command()
    # def ask(prompt: str):
    #     typer.echo("Thinking...")
    #     response = "Pretend AI answer"
    #     typer.echo(response)
    
    # Simulated:
    prompt = "What is Python?"
    print("Thinking...")
    print("Python is a high-level programming language known for its readability.")
    print()
    print("Typer is fantastic for developer tools, automation scripts, and utilities.")

    Part 2: Advanced Patterns โ€” Real Engineering Features

    In this part, we go deeper into real engineering patterns, advanced argument features, error handling, output design, color/UI improvements, and structuring multi-file CLI apps the same way professionals build tools like pip, docker, aws-cli, git, uv, and fastapi-cli.

    โšก 1. Advanced argparse Features You MUST Know

    โœ” Mutually Exclusive Groups

    Sometimes a user should only choose one option:

    Mutually Exclusive Groups

    Only one option allowed

    Try it Yourself ยป
    Python
    import argparse
    
    parser = argparse.ArgumentParser()
    
    group = parser.add_mutually_exclusive_group()
    group.add_argument("--verbose", action="store_true")
    group.add_argument("--quiet", action="store_true")
    
    # Demo: can only use one
    args = parser.parse_args(["--verbose"])
    print(f"Verbose: {args.verbose}, Quiet: {args.quiet}")
    
    # Using both would cause an error:
    # python tool.py --verbose --quiet  # ERROR!

    Now the user cannot do: python tool.py --verbose --quiet

    This is used for logging mode, compression types, environment switches, etc.

    โœ” nargs โ€” Accepting multiple values

    nargs - Multiple Values

    Accept multiple arguments

    Try it Yourself ยป
    Python
    import argparse
    
    parser = argparse.ArgumentParser()
    parser.add_argument("--files", nargs="+")
    
    # Demo: multiple files
    args = parser.parse_args(["--files", "a.txt", "b.txt", "c.txt"])
    print(f"Files: {args.files}")
    
    # Usage: python tool.py --files a.txt b.txt c.txt

    Useful for batch operations.

    โœ” Choices โ€” Restricting allowed values

    Choices

    Restrict allowed values

    Try it Yourself ยป
    Python
    import argparse
    
    parser = argparse.ArgumentParser()
    parser.add_argument("--level", choices=["low", "medium", "high"])
    
    # Demo: valid choice
    args = parser.parse_args(["--level", "high"])
    print(f"Level: {args.level}")
    
    # Invalid choice would cause error automatically

    Stops invalid inputs before they crash your program.

    โœ” Default values + required flags

    Required Flags

    Mandatory arguments

    Try it Yourself ยป
    Python
    import argparse
    
    parser = argparse.ArgumentParser()
    parser.add_argument("--mode", required=True)
    parser.add_argument("--count", type=int, default=1)
    
    # Demo
    args = parser.parse_args(["--mode", "production"])
    print(f"Mode: {args.mode}, Count: {args.count}")
    
    # argparse handles missing required flags automatically

    ๐Ÿง  2. Using Config Files + CLI Arguments Together

    Professional CLI tools combine: environment variables, config files, command-line arguments.

    Example: python deploy.py --config config.yaml --env production

    argparse supports this pattern through: FileType, custom loaders, layered parsing logic

    ๐Ÿงฐ 3. Advanced Typer Patterns (Modern CLI Engineering)

    โœ” Prompting users interactively

    Interactive Prompts

    User input in CLI

    Try it Yourself ยป
    Python
    # Typer interactive prompts
    # @app.command()
    # def register():
    #     name = typer.prompt("Enter your name")
    #     password = typer.prompt("Password", hide_input=True)
    #     typer.echo(f"Welcome, {name}!")
    
    # Simulated:
    print("Enter your name: Boopie")
    print("Password: ********")
    print("Welcome, Boopie!")
    print()
    print("This is real UX - great for account creation scripts,"
          " secure flows, admin utilities.")

    โœ” Confirmation dialogs

    Confirmation Dialogs

    Ask before dangerous actions

    Try it Yourself ยป
    Python
    # Typer confirmation dialogs
    # @app.command()
    # def delete_user(name: str):
    #     if typer.confirm(f"Delete user {name}?"):
    #         typer.echo("Deleted.")
    #     else:
    #         typer.echo("Cancelled.")
    
    # Simulated:
    name = "Boopie"
    print(f"Delete user {name}? [y/N]: y")
    print("Deleted.")

    โœ” Progress bars

    Progress Bars

    Visual feedback

    Try it Yourself ยป
    Python
    # Typer progress bars
    # with typer.progressbar(range(100)) as bar:
    #     for i in bar:
    #         time.sleep(0.01)
    
    # Simulated:
    print("Processing: [โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ] 100%")
    print("Done!")
    print()
    print("Progress bars make tools feel professional instantly.")

    โœ” Rich (Color + Formatting)

    Rich Formatting

    Colors and styling

    Try it Yourself ยป
    Python
    # Rich console output
    # from rich.console import Console
    # console = Console()
    
    # @app.command()
    # def info():
    #     console.print("[bold green]Server Running[/bold green]")
    
    # Simulated:
    print("\033[1;32mServer Running\033[0m")  # Bold green
    print()
    print("Used in: FastAPI CLI, Poetry, Modern developer tools")

    โš™๏ธ 4. Handling Errors Gracefully

    Typer supports structured exceptions:

    Error Handling

    Graceful error messages

    Try it Yourself ยป
    Python
    # Typer error handling
    # class NotFound(Exception):
    #     pass
    
    # @app.command()
    # def get_user(user_id: int):
    #     if user_id != 1:
    #         raise NotFound("User not found")
    
    # @app.error_handler(NotFound)
    # def handle_not_found(e):
    #     typer.echo(f"Error: {e}")
    
    # Simulated:
    user_id = 99
    if user_id != 1:
        print(f"Error: User {user_id} not found")
    print()
    print("This makes CLI tools 'feel' polished.")

    ๐Ÿงฉ 5. Building Multi-Command Apps (Professional Structure)

    Real CLIs use folder architectures. For Typer:

    app/
     โ”œโ”€ main.py
     โ”œโ”€ users.py
     โ”œโ”€ files.py
     โ””โ”€ config.py

    main.py:

    Main Entry Point

    Multi-command structure

    Try it Yourself ยป
    Python
    # main.py
    # import typer
    # import users, files
    
    # app = typer.Typer()
    # app.add_typer(users.app, name="users")
    # app.add_typer(files.app, name="files")
    
    # if __name__ == "__main__":
    #     app()
    
    print("Multi-command CLI structure:")
    print("  python tool.py users add Boopie")
    print("  python tool.py files clean")
    print("  python tool.py config set key value")

    users.py:

    Users Module

    Subcommand module

    Try it Yourself ยป
    Python
    # users.py
    # import typer
    # app = typer.Typer()
    
    # @app.command()
    # def add(name: str):
    #     typer.echo(f"Added user: {name}")
    
    # Simulated:
    name = "Boopie"
    print(f"Added user: {name}")
    print()
    print("This is identical to how enterprise tools structure their commands.")

    ๐Ÿง  6. Environment-Aware Commands

    Combine environment variables:

    Environment Variables

    Environment-aware commands

    Try it Yourself ยป
    Python
    import os
    
    # @app.command()
    # def upload(file: str):
    #     key = os.getenv("API_KEY")
    #     if not key:
    #         typer.echo("Missing API_KEY")
    #         raise typer.Exit()
    
    # Simulated:
    key = os.getenv("API_KEY")
    if not key:
        print("Missing API_KEY")
        print("Set it with: export API_KEY='your-key'")
    else:
        print(f"Using API key: {key[:4]}...")
    
    print()
    print("Used heavily in: cloud automation, CI/CD tools, backend deployment flows")

    ๐Ÿ“ฆ 7. Creating "Plugins" for CLI Tools

    Typer supports loading commands dynamically:

    Plugin System

    Dynamic command loading

    Try it Yourself ยป
    Python
    # Plugin loading pattern
    # def load_plugins(app):
    #     for plugin in discover_plugins():
    #         app.add_typer(plugin.typer_app)
    
    # Simulated plugin discovery:
    plugins = ["analytics", "backup", "export"]
    for plugin in plugins:
        print(f"Loaded plugin: {plugin}")
    
    print()
    print("This pattern is how tools like pytest, aws-cli, docker")
    print("extend functionality through plugins.")

    ๐Ÿ” 8. Auto-generated Help Screens

    Typer help output looks like:

    Usage: tool.py greet [OPTIONS] NAME
    
    Arguments:
      NAME  Your name.
    
    Options:
      --wave / --no-wave  Whether to wave.
      --help              Show this message and exit.

    This UI alone saves hours of documentation. argparse also generates help automatically, but Typer's formatting is noticeably cleaner.

    ๐Ÿงฑ 9. Testing CLI Tools

    You can test argparse & Typer apps with pytest using CliRunner. Example for Typer:

    Testing CLI Tools

    pytest with CliRunner

    Try it Yourself ยป
    Python
    # Testing CLI with pytest
    # from typer.testing import CliRunner
    # from main import app
    
    # runner = CliRunner()
    
    # def test_greet():
    #     result = runner.invoke(app, ["greet", "Boopie"])
    #     assert "Hello Boopie" in result.stdout
    
    # Simulated test:
    def test_greet():
        # Simulated result
        stdout = "Hello Boopie!"
        assert "Hello Boopie" in stdout
        print("โœ” test_greet PASSED")
    
    test_greet()
    print()
    print("Testing CLIs makes them reliable.")

    ๐ŸŒ 10. Packaging Your CLI as a PIP Installable Tool

    You can turn a Typer/argparse script into a real installable command:

    pyproject.toml:

    [project.scripts]
    learnfast = "app.main:app"

    Users can now install: pip install learnfast

    And then run: learnfast

    Exactly how uv, pip, and fastapi work.

    Part 3: Enterprise-Grade CLI Engineering โ€” Production Deployment

    This final section focuses on enterprise-grade CLI engineering, including structured logging, configuration layers, packaging/distribution, versioning, interactive shell modes, autocomplete, performance optimisation, security best practices, and production deployment patterns.

    โšก 1. Enterprise-Style Configuration Layers

    Professional CLI apps often support configuration from multiple layers:

    1. Default settings
    2. Config file (YAML/JSON/TOML)
    3. Environment variables
    4. Command-line arguments (highest priority)

    Example loading order:

    Configuration Layers

    Enterprise config pattern

    Try it Yourself ยป
    Python
    import os
    
    # Configuration layer pattern
    def load_defaults():
        return {"debug": False, "port": 8000}
    
    def load_yaml(path):
        # Would use yaml.safe_load() in real code
        return {"port": 3000}
    
    def read_env():
        return {"debug": os.getenv("DEBUG", "false") == "true"}
    
    # Build config with priority
    config = load_defaults()
    print(f"1. Defaults: {config}")
    
    # if os.path.exists("config.yaml"):
    config.update(load_yaml("config.yaml"))
    print(f"2. After YAML: {config}")
    
    config.update(read_env())
    
    ...

    This allows flexible behaviour: users can override defaults, automation pipelines can use environment variables, developers can test using temporary config files. Typer-based tools commonly use pydantic or dynaconf to manage all layers cleanly.

    ๐Ÿง  2. CLI Autocompletion (Bash, Zsh, Fish)

    Modern CLI tools include autocomplete for: commands, options, file paths, arguments.

    Typer makes this extremely simple:

    tool --install-completion
    tool --show-completion

    Under the hood, it generates shell-compatible scripts. This dramatically improves user experience and is a key part of professional developer tools.

    ๐Ÿ“ฆ 3. Packaging as a Standalone Executable (Windows, Mac, Linux)

    Not every user has Python installed. You can turn your CLI into one executable file using:

    PyInstaller

    pyinstaller --onefile main.py

    Nuitka (fastest) โ€” Compiles Python to C.

    nuitka --onefile main.py

    Briefcase โ€” Creates full OS-native installers.

    This is how tools become "real" apps that run anywhere.

    ๐Ÿงฉ 4. Versioning & Release Flows

    Every CLI should have a version:

    Versioning

    CLI version management

    Try it Yourself ยป
    Python
    # Versioning your CLI
    __version__ = "1.2.0"
    
    # @app.command()
    # def version():
    #     typer.echo("LearnCodingFast CLI v1.2.0")
    
    # Or use Typer's built-in:
    # app = typer.Typer(context_settings={"help_option_names": ["--help"]})
    
    print(f"LearnCodingFast CLI v{__version__}")
    print()
    print("Release strategy:")
    print("  1. increment version")
    print("  2. build wheel")
    print("  3. publish to PyPI")
    print("  4. update docs")
    print("  5. tag release in GitHub")

    โš™๏ธ 5. Creating Subcommands Like Professional Tools (docker, git, aws-cli)

    Large CLI tools organise commands into modules:

    tool
     โ”œโ”€ users add
     โ”œโ”€ users delete
     โ”œโ”€ files upload
     โ”œโ”€ files download
     โ”œโ”€ system info

    Typer subcommands:

    Professional Subcommands

    Docker/Git style structure

    Try it Yourself ยป
    Python
    # Professional subcommand structure
    # app = typer.Typer()
    # users_app = typer.Typer()
    # files_app = typer.Typer()
    
    # app.add_typer(users_app, name="users")
    # app.add_typer(files_app, name="files")
    
    print("Professional CLI structure:")
    print("  tool users add <name>")
    print("  tool users delete <id>")
    print("  tool files upload <path>")
    print("  tool files download <url>")
    print("  tool system info")
    print()
    print("Each subcommand can have its own flags,")
    print("callbacks, error handlers, and he
    ...

    ๐Ÿงต 6. Integrating Your CLI With Other Tools (Pipes & Redirects)

    CLI tools should work with Linux/Windows pipes:

    echo "data" | tool process
    tool list | grep "Boopie"
    tool fetch > output.json

    Typer + sys.stdin:

    Pipes & Redirects

    Unix pipeline integration

    Try it Yourself ยป
    Python
    import sys
    
    # Reading from stdin for piped input
    # data = sys.stdin.read()
    
    # Simulated pipe input
    simulated_input = "Hello from pipe!"
    print(f"Received: {simulated_input}")
    print()
    print("Your CLI can now be used inside:")
    print("  โœ” shells")
    print("  โœ” automation scripts")
    print("  โœ” cron jobs")
    print("  โœ” CI/CD pipelines")
    print()
    print("This elevates it to real engineering level.")

    ๐Ÿ”’ 7. Authentication & Secrets Management

    If your CLI interacts with APIs, databases, or cloud services, it must handle secrets safely. Best practices:

    • โœ” Use environment variables: export API_KEY="123"
    • โœ” Never store secrets in code
    • โœ” Use a .env loader (python-dotenv)
    • โœ” Hide passwords in Typer prompts

    Secrets Management

    Secure credential handling

    Try it Yourself ยป
    Python
    # Secure password input
    # password = typer.prompt("Password", hide_input=True)
    
    import getpass
    
    # Simulated secure input
    print("Password: ", end="")
    # password = getpass.getpass("")  # Would hide input
    print("********")
    print()
    print("Best practices:")
    print("  โœ” Use environment variables")
    print("  โœ” Never store secrets in code")
    print("  โœ” Use python-dotenv for .env files")
    print("  โœ” Hide passwords in prompts")
    print("  โœ” Encrypt stored tokens")

    ๐Ÿงช 8. Writing Realistic Tests for CLI Behaviour

    Use Typer's testing tools:

    CLI Testing

    Comprehensive test patterns

    Try it Yourself ยป
    Python
    # Comprehensive CLI testing
    # from typer.testing import CliRunner
    
    # runner = CliRunner()
    
    # def test_delete():
    #     result = runner.invoke(app, ["users", "delete", "123"])
    #     assert result.exit_code == 0
    #     assert "Deleted" in result.stdout
    
    # Simulated tests:
    def test_delete():
        exit_code = 0
        stdout = "Deleted user 123"
        assert exit_code == 0
        assert "Deleted" in stdout
        print("โœ” test_delete PASSED")
    
    def test_invalid_command():
        exit_code = 2
        assert exit_code != 
    ...

    ๐ŸŒ 9. Color, Formatting & Tabular Output (Rich + Typer)

    Real-world tools display: tables, panels, logs, status values, colour-coded results.

    Rich Tables

    Beautiful CLI output

    Try it Yourself ยป
    Python
    # Rich table output
    # from rich.table import Table
    # from rich.console import Console
    
    # @app.command()
    # def stats():
    #     table = Table(title="User Stats")
    #     table.add_column("Name")
    #     table.add_column("Uploads")
    #     table.add_row("Boopie", "22")
    #     table.add_row("Josh", "5")
    #     Console().print(table)
    
    # Simulated table output:
    print("โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”")
    print("โ”‚     User Stats        โ”‚")
    print("โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค")
    print("โ”‚ Name     โ”‚ Uploads    โ”‚")
    print("โ”œโ”€โ”€โ”€
    ...

    ๐Ÿงฑ 10. Performance & Concurrency Inside CLI Tools

    CLI commands often do: API requests, file scanning, JSON parsing, image processing, database queries.

    Improve speed using:

    • โœ” ThreadPoolExecutor (I/O tasks)
    • โœ” ProcessPoolExecutor (CPU tasks)
    • โœ” asyncio (many lightweight tasks)
    • โœ” caching (functools.lru_cache)
    • โœ” batching
    • โœ” chunk streaming

    Your CLI becomes fast and scalable.

    ๐Ÿš€ 11. Real Engineering Pattern โ€” Modular "Actions" Layer

    For large CLIs, never put logic inside the command functions. Instead:

    actions/
     โ”œโ”€ users.py
     โ”œโ”€ files.py
     โ””โ”€ analytics.py

    Command โ†’ calls โ†’ action function

    This allows: rewiring logic, sharing helpers, unit testing functions directly, keeping CLI layer clean. This is how professional tools stay maintainable.

    ๐Ÿ“ฆ 12. Distribution: PyPI, Brew, Winget, and GitHub Releases

    You can distribute your tool via:

    • โœ” PyPI (pip install)
    • โœ” Homebrew (Mac)
    • โœ” Winget (Windows)
    • โœ” Snap/Apt (Linux)
    • โœ” GitHub Releases (binaries)

    This is the path for turning your CLI into a global developer tool.

    ๐ŸŽ‰ Final Summary

    After completing all 3 parts, you can build CLI tools with:

    โœ” Professional UX

    โœ” Structured commands

    โœ” Interactive flows

    โœ” Autocomplete

    โœ” Config layers

    โœ” Error handling

    โœ” Testing

    โœ” Distribution

    You now have the skills to build production-grade CLI tools!

    ๐Ÿ“‹ Quick Reference โ€” CLI Tools

    Syntax / ToolWhat it does
    argparse.ArgumentParser()Parse CLI arguments (stdlib)
    parser.add_argument('--name')Add a named flag argument
    @app.command() (Typer)Define a Typer CLI command
    typer.Option(..., help=)Add option with help text
    rich.print("[bold]text[/bold]")Pretty terminal output

    ๐ŸŽ‰ Great work! You've completed this lesson.

    You can now build polished CLI tools with argument parsing, progress bars, coloured output, and bash completions.

    Up next: Virtual Environments โ€” manage isolated Python environments for every project.

    Sign up for free to track which lessons you've completed and get learning reminders.

    Previous

    Cookie & Privacy Settings

    We use cookies to improve your experience, analyze traffic, and show personalized ads. You can manage your preferences below.

    By clicking "Accept All", you consent to our use of cookies for analytics and personalized advertising. You can customize your preferences or reject non-essential cookies.

    Privacy Policy โ€ข Terms of Service