python

examples

examples.py🐍
"""
CLI Applications - Examples

Learn to build professional command-line tools with Python.
"""

import argparse
import sys
from pathlib import Path
from typing import Callable
from dataclasses import dataclass, field


# =============================================================================
# ARGPARSE BASICS - Built-in argument parsing
# =============================================================================

def argparse_basic_example():
    """Basic argparse usage."""
    parser = argparse.ArgumentParser(
        description="A simple file processor",
        epilog="Example: python cli.py input.txt -o output.txt"
    )
    
    # Positional argument (required)
    parser.add_argument(
        'input_file',
        type=str,
        help='Input file to process'
    )
    
    # Optional arguments with flags
    parser.add_argument(
        '-o', '--output',
        type=str,
        default='output.txt',
        help='Output file (default: output.txt)'
    )
    
    parser.add_argument(
        '-v', '--verbose',
        action='store_true',
        help='Enable verbose output'
    )
    
    parser.add_argument(
        '-n', '--count',
        type=int,
        default=1,
        help='Number of times to process'
    )
    
    # Parse arguments (use [] for testing, remove for real CLI)
    args = parser.parse_args(['test.txt', '-v', '-n', '3'])
    
    print(f"Input: {args.input_file}")
    print(f"Output: {args.output}")
    print(f"Verbose: {args.verbose}")
    print(f"Count: {args.count}")


def argparse_subcommands_example():
    """Argparse with subcommands (like git)."""
    parser = argparse.ArgumentParser(
        prog='myapp',
        description='Application with subcommands'
    )
    
    subparsers = parser.add_subparsers(dest='command', help='Available commands')
    
    # 'init' subcommand
    init_parser = subparsers.add_parser('init', help='Initialize a project')
    init_parser.add_argument('name', help='Project name')
    init_parser.add_argument('--template', default='basic', help='Template to use')
    
    # 'build' subcommand
    build_parser = subparsers.add_parser('build', help='Build the project')
    build_parser.add_argument('--release', action='store_true', help='Build for release')
    build_parser.add_argument('--target', default='all', help='Build target')
    
    # 'run' subcommand
    run_parser = subparsers.add_parser('run', help='Run the project')
    run_parser.add_argument('--port', type=int, default=8000, help='Port to run on')
    
    # Test parsing
    args = parser.parse_args(['init', 'myproject', '--template', 'web'])
    print(f"Command: {args.command}")
    print(f"Name: {args.name}")
    print(f"Template: {args.template}")


def argparse_choices_example():
    """Argparse with choices and validation."""
    parser = argparse.ArgumentParser(description='Process with choices')
    
    parser.add_argument(
        '--format',
        choices=['json', 'xml', 'csv'],
        default='json',
        help='Output format'
    )
    
    parser.add_argument(
        '--log-level',
        choices=['DEBUG', 'INFO', 'WARNING', 'ERROR'],
        default='INFO',
        help='Logging level'
    )
    
    parser.add_argument(
        '--workers',
        type=int,
        choices=range(1, 17),
        default=4,
        metavar='N',
        help='Number of workers (1-16)'
    )
    
    args = parser.parse_args(['--format', 'csv', '--log-level', 'DEBUG'])
    print(f"Format: {args.format}")
    print(f"Log level: {args.log_level}")


# =============================================================================
# CLICK - Decorator-based CLI framework
# =============================================================================

# Note: Click needs to be installed: pip install click

CLICK_EXAMPLE = '''
import click

@click.group()
@click.version_option(version='1.0.0')
def cli():
    """My awesome CLI application."""
    pass

@cli.command()
@click.argument('name')
@click.option('--greeting', '-g', default='Hello', help='Greeting to use')
@click.option('--count', '-c', default=1, help='Number of greetings')
def greet(name, greeting, count):
    """Greet someone."""
    for _ in range(count):
        click.echo(f'{greeting}, {name}!')

@cli.command()
@click.argument('input_file', type=click.Path(exists=True))
@click.option('--output', '-o', type=click.Path(), help='Output file')
@click.option('--verbose', '-v', is_flag=True, help='Verbose output')
def process(input_file, output, verbose):
    """Process a file."""
    if verbose:
        click.echo(f'Processing {input_file}...')
    
    # Process file...
    click.echo(click.style('Done!', fg='green'))

@cli.command()
@click.option('--name', prompt='Your name', help='Your name')
@click.option('--password', prompt=True, hide_input=True, help='Password')
def login(name, password):
    """Login to the system."""
    click.echo(f'Logging in as {name}...')

if __name__ == '__main__':
    cli()
'''


# =============================================================================
# TYPER - Modern CLI with type hints
# =============================================================================

# Note: Typer needs to be installed: pip install typer

TYPER_EXAMPLE = '''
import typer
from typing import Optional
from enum import Enum

app = typer.Typer(help="My awesome CLI application")

class Format(str, Enum):
    json = "json"
    yaml = "yaml"
    toml = "toml"

@app.command()
def greet(
    name: str = typer.Argument(..., help="Name to greet"),
    greeting: str = typer.Option("Hello", "--greeting", "-g", help="Greeting"),
    count: int = typer.Option(1, "--count", "-c", help="Number of greetings"),
):
    """Greet someone with a custom message."""
    for _ in range(count):
        typer.echo(f"{greeting}, {name}!")

@app.command()
def process(
    input_file: Path = typer.Argument(..., exists=True, help="Input file"),
    output: Optional[Path] = typer.Option(None, "--output", "-o"),
    format: Format = typer.Option(Format.json, "--format", "-f"),
    verbose: bool = typer.Option(False, "--verbose", "-v"),
):
    """Process a file."""
    if verbose:
        typer.echo(f"Processing {input_file} as {format.value}...")
    
    # Process...
    typer.echo(typer.style("Done!", fg=typer.colors.GREEN))

@app.command()
def init(
    name: str = typer.Argument(...),
    force: bool = typer.Option(False, "--force", "-f", help="Overwrite existing"),
):
    """Initialize a new project."""
    if force:
        typer.confirm("This will overwrite existing files. Continue?", abort=True)
    
    typer.echo(f"Creating project {name}...")

if __name__ == "__main__":
    app()
'''


# =============================================================================
# RICH - Beautiful terminal output
# =============================================================================

# Note: Rich needs to be installed: pip install rich

RICH_EXAMPLE = '''
from rich.console import Console
from rich.table import Table
from rich.progress import Progress, track
from rich.panel import Panel
from rich.markdown import Markdown
from rich import print as rprint
import time

console = Console()

# Styled text
console.print("[bold red]Error:[/bold red] Something went wrong")
console.print("[green]Success![/green] Operation completed")
console.print("[blue underline]https://example.com[/blue underline]")

# Tables
table = Table(title="Users")
table.add_column("ID", style="cyan")
table.add_column("Name", style="magenta")
table.add_column("Email", style="green")

table.add_row("1", "Alice", "alice@example.com")
table.add_row("2", "Bob", "bob@example.com")
table.add_row("3", "Charlie", "charlie@example.com")

console.print(table)

# Progress bars
with Progress() as progress:
    task = progress.add_task("[green]Processing...", total=100)
    while not progress.finished:
        progress.update(task, advance=1)
        time.sleep(0.01)

# Simple progress with track
for item in track(range(100), description="Processing..."):
    time.sleep(0.01)

# Panels
panel = Panel("This is important information", title="Notice", border_style="blue")
console.print(panel)

# Markdown
md = Markdown("""
# Title
This is **bold** and this is *italic*.

- Item 1
- Item 2
- Item 3
""")
console.print(md)

# Status spinner
with console.status("[bold green]Working...") as status:
    time.sleep(2)
    status.update("[bold blue]Almost done...")
    time.sleep(1)
'''


# =============================================================================
# BUILDING A SIMPLE CLI WITHOUT EXTERNAL LIBS
# =============================================================================

@dataclass
class Command:
    """A CLI command definition."""
    name: str
    description: str
    handler: Callable
    arguments: list = field(default_factory=list)


class SimpleCLI:
    """A simple CLI framework without external dependencies."""
    
    def __init__(self, name: str, description: str = ""):
        self.name = name
        self.description = description
        self.commands: dict[str, Command] = {}
    
    def command(self, name: str, description: str = ""):
        """Decorator to register a command."""
        def decorator(func: Callable):
            self.commands[name] = Command(name, description, func)
            return func
        return decorator
    
    def print_help(self):
        """Print help message."""
        print(f"\n{self.name}")
        if self.description:
            print(f"  {self.description}")
        print("\nCommands:")
        for name, cmd in self.commands.items():
            print(f"  {name:15} {cmd.description}")
        print("\nUse '{} <command> --help' for more information.\n".format(self.name))
    
    def run(self, args: list[str] | None = None):
        """Run the CLI with given arguments."""
        if args is None:
            args = sys.argv[1:]
        
        if not args or args[0] in ['-h', '--help', 'help']:
            self.print_help()
            return
        
        command_name = args[0]
        if command_name not in self.commands:
            print(f"Error: Unknown command '{command_name}'")
            self.print_help()
            return
        
        command = self.commands[command_name]
        command.handler(*args[1:])


# Example usage of SimpleCLI
cli = SimpleCLI("mycli", "A simple demonstration CLI")


@cli.command("hello", "Say hello to someone")
def hello_command(name: str = "World"):
    """Say hello."""
    print(f"Hello, {name}!")


@cli.command("add", "Add two numbers")
def add_command(a: str, b: str):
    """Add two numbers."""
    try:
        result = float(a) + float(b)
        print(f"{a} + {b} = {result}")
    except ValueError:
        print("Error: Please provide valid numbers")


@cli.command("list", "List files in directory")
def list_command(path: str = "."):
    """List files."""
    p = Path(path)
    if p.exists() and p.is_dir():
        for item in p.iterdir():
            prefix = "📁" if item.is_dir() else "📄"
            print(f"  {prefix} {item.name}")
    else:
        print(f"Error: '{path}' is not a valid directory")


# =============================================================================
# CONFIGURATION FILE HANDLING
# =============================================================================

import json


def load_config_json(path: str) -> dict:
    """Load JSON configuration file."""
    with open(path) as f:
        return json.load(f)


def load_config_env(prefix: str = "") -> dict:
    """Load configuration from environment variables."""
    import os
    config = {}
    for key, value in os.environ.items():
        if prefix and not key.startswith(prefix):
            continue
        clean_key = key[len(prefix):] if prefix else key
        config[clean_key.lower()] = value
    return config


# YAML config example (requires pyyaml)
YAML_CONFIG_EXAMPLE = '''
import yaml

def load_config_yaml(path: str) -> dict:
    """Load YAML configuration file."""
    with open(path) as f:
        return yaml.safe_load(f)

# Example YAML config:
"""
app:
  name: MyApp
  debug: true
  
database:
  host: localhost
  port: 5432
  name: myapp
  
features:
  - authentication
  - logging
  - caching
"""
'''


# TOML config example (Python 3.11+ has tomllib built-in)
TOML_CONFIG_EXAMPLE = '''
import tomllib  # Python 3.11+

def load_config_toml(path: str) -> dict:
    """Load TOML configuration file."""
    with open(path, "rb") as f:
        return tomllib.load(f)

# Example TOML config:
"""
[app]
name = "MyApp"
debug = true

[database]
host = "localhost"
port = 5432
name = "myapp"

[features]
enabled = ["authentication", "logging", "caching"]
"""
'''


# =============================================================================
# TERMINAL COLORS WITHOUT EXTERNAL LIBS
# =============================================================================

class Colors:
    """ANSI color codes for terminal output."""
    
    # Text colors
    BLACK = '\033[30m'
    RED = '\033[31m'
    GREEN = '\033[32m'
    YELLOW = '\033[33m'
    BLUE = '\033[34m'
    MAGENTA = '\033[35m'
    CYAN = '\033[36m'
    WHITE = '\033[37m'
    
    # Background colors
    BG_BLACK = '\033[40m'
    BG_RED = '\033[41m'
    BG_GREEN = '\033[42m'
    BG_YELLOW = '\033[43m'
    BG_BLUE = '\033[44m'
    
    # Styles
    BOLD = '\033[1m'
    DIM = '\033[2m'
    ITALIC = '\033[3m'
    UNDERLINE = '\033[4m'
    
    # Reset
    RESET = '\033[0m'


def colored(text: str, color: str, bold: bool = False) -> str:
    """Apply color to text."""
    style = Colors.BOLD if bold else ""
    return f"{style}{color}{text}{Colors.RESET}"


def print_success(message: str):
    """Print success message in green."""
    print(colored(f"✓ {message}", Colors.GREEN))


def print_error(message: str):
    """Print error message in red."""
    print(colored(f"✗ {message}", Colors.RED, bold=True))


def print_warning(message: str):
    """Print warning message in yellow."""
    print(colored(f"⚠ {message}", Colors.YELLOW))


def print_info(message: str):
    """Print info message in blue."""
    print(colored(f"ℹ {message}", Colors.BLUE))


# =============================================================================
# SIMPLE PROGRESS BAR
# =============================================================================

import time


def progress_bar(
    iterable,
    total: int | None = None,
    prefix: str = "",
    length: int = 40,
    fill: str = "█"
):
    """
    Simple progress bar without external dependencies.
    
    Usage:
        for item in progress_bar(items, prefix="Processing"):
            process(item)
    """
    total = total or len(iterable)
    
    def print_progress(iteration):
        percent = iteration / total
        filled = int(length * percent)
        bar = fill * filled + '-' * (length - filled)
        print(f'\r{prefix} |{bar}| {percent:.1%}', end='', flush=True)
    
    for i, item in enumerate(iterable, 1):
        yield item
        print_progress(i)
    
    print()  # New line at the end


# =============================================================================
# INTERACTIVE PROMPTS
# =============================================================================

def prompt(message: str, default: str = "") -> str:
    """Prompt user for input with optional default."""
    if default:
        result = input(f"{message} [{default}]: ").strip()
        return result if result else default
    return input(f"{message}: ").strip()


def confirm(message: str, default: bool = False) -> bool:
    """Ask for yes/no confirmation."""
    suffix = " [Y/n]" if default else " [y/N]"
    response = input(f"{message}{suffix}: ").strip().lower()
    
    if not response:
        return default
    return response in ('y', 'yes', 'true', '1')


def choose(message: str, choices: list[str], default: int = 0) -> str:
    """Let user choose from a list of options."""
    print(message)
    for i, choice in enumerate(choices, 1):
        marker = ">" if i - 1 == default else " "
        print(f"  {marker} {i}. {choice}")
    
    while True:
        try:
            response = input(f"Choose [1-{len(choices)}]: ").strip()
            if not response:
                return choices[default]
            index = int(response) - 1
            if 0 <= index < len(choices):
                return choices[index]
        except ValueError:
            pass
        print("Invalid choice. Try again.")


# =============================================================================
# DEMONSTRATIONS
# =============================================================================

if __name__ == "__main__":
    print("=" * 60)
    print("ARGPARSE BASIC EXAMPLE")
    print("=" * 60)
    argparse_basic_example()
    
    print("\n" + "=" * 60)
    print("ARGPARSE SUBCOMMANDS")
    print("=" * 60)
    argparse_subcommands_example()
    
    print("\n" + "=" * 60)
    print("COLORED OUTPUT")
    print("=" * 60)
    print_success("Operation completed successfully!")
    print_error("Something went wrong!")
    print_warning("This might cause issues")
    print_info("Just FYI...")
    
    print("\n" + "=" * 60)
    print("PROGRESS BAR")
    print("=" * 60)
    items = list(range(50))
    for item in progress_bar(items, prefix="Processing"):
        time.sleep(0.02)
    
    print("\n" + "=" * 60)
    print("SIMPLE CLI DEMO")
    print("=" * 60)
    cli.run(['hello', 'Python'])
    cli.run(['add', '10', '20'])
    cli.run(['list', '.'])
Examples - Python Tutorial | DeepML