Initial commit
This commit is contained in:
418
skills/fire-patterns/examples/advanced-patterns.md
Normal file
418
skills/fire-patterns/examples/advanced-patterns.md
Normal file
@@ -0,0 +1,418 @@
|
||||
# Advanced Fire CLI Patterns
|
||||
|
||||
This guide covers advanced patterns and techniques for building sophisticated Fire CLIs.
|
||||
|
||||
## Pattern 1: Configuration Management with Persistence
|
||||
|
||||
Implement persistent configuration storage:
|
||||
|
||||
```python
|
||||
import fire
|
||||
from rich.console import Console
|
||||
from pathlib import Path
|
||||
import json
|
||||
|
||||
console = Console()
|
||||
|
||||
class ConfigManager:
|
||||
"""Handle configuration file I/O"""
|
||||
|
||||
def __init__(self, config_path: Path):
|
||||
self.config_path = config_path
|
||||
self._ensure_exists()
|
||||
|
||||
def _ensure_exists(self):
|
||||
"""Create config file if missing"""
|
||||
self.config_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
if not self.config_path.exists():
|
||||
self.save({})
|
||||
|
||||
def load(self) -> dict:
|
||||
"""Load configuration"""
|
||||
try:
|
||||
return json.loads(self.config_path.read_text())
|
||||
except Exception as e:
|
||||
console.print(f"[red]Error loading config: {e}[/red]")
|
||||
return {}
|
||||
|
||||
def save(self, config: dict):
|
||||
"""Save configuration"""
|
||||
self.config_path.write_text(json.dumps(config, indent=2))
|
||||
|
||||
class MyCLI:
|
||||
def __init__(self):
|
||||
self.config_manager = ConfigManager(
|
||||
Path.home() / ".mycli" / "config.json"
|
||||
)
|
||||
|
||||
def configure(self, key, value):
|
||||
"""Set configuration value"""
|
||||
config = self.config_manager.load()
|
||||
config[key] = value
|
||||
self.config_manager.save(config)
|
||||
console.print(f"[green]✓[/green] Set {key} = {value}")
|
||||
```
|
||||
|
||||
## Pattern 2: Environment-Based Configuration
|
||||
|
||||
Handle multiple environments:
|
||||
|
||||
```python
|
||||
from enum import Enum
|
||||
|
||||
class Environment(str, Enum):
|
||||
DEV = "dev"
|
||||
STAGING = "staging"
|
||||
PRODUCTION = "production"
|
||||
|
||||
class MyCLI:
|
||||
def __init__(self):
|
||||
self.current_env = Environment.DEV
|
||||
|
||||
def set_env(self, env: Environment):
|
||||
"""Switch environment
|
||||
|
||||
Args:
|
||||
env: Target environment (dev, staging, production)
|
||||
"""
|
||||
self.current_env = env
|
||||
console.print(f"[cyan]Environment: {env.value}[/cyan]")
|
||||
|
||||
def deploy(self):
|
||||
"""Deploy to current environment"""
|
||||
console.print(f"Deploying to {self.current_env.value}")
|
||||
|
||||
# Usage:
|
||||
# python cli.py set-env staging
|
||||
# python cli.py deploy
|
||||
```
|
||||
|
||||
## Pattern 3: Property Access for Read-Only Values
|
||||
|
||||
Use properties for values that should be readable but not settable:
|
||||
|
||||
```python
|
||||
class MyCLI:
|
||||
def __init__(self):
|
||||
self._version = "1.0.0"
|
||||
self._config_path = Path.home() / ".mycli"
|
||||
|
||||
@property
|
||||
def version(self):
|
||||
"""Get CLI version"""
|
||||
return self._version
|
||||
|
||||
@property
|
||||
def config_path(self):
|
||||
"""Get configuration path"""
|
||||
return str(self._config_path)
|
||||
|
||||
# Usage:
|
||||
# python cli.py version # Returns "1.0.0"
|
||||
# python cli.py config-path # Returns path
|
||||
```
|
||||
|
||||
## Pattern 4: Validation and Error Handling
|
||||
|
||||
Implement robust validation:
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
|
||||
class MyCLI:
|
||||
def deploy(self, path: str, confirm=False):
|
||||
"""Deploy from path
|
||||
|
||||
Args:
|
||||
path: Path to deployment files
|
||||
confirm: Skip confirmation prompt
|
||||
"""
|
||||
deploy_path = Path(path)
|
||||
|
||||
# Validate path exists
|
||||
if not deploy_path.exists():
|
||||
console.print(f"[red]Error: Path not found: {path}[/red]")
|
||||
return {"status": "error", "message": "Path not found"}
|
||||
|
||||
# Validate path is directory
|
||||
if not deploy_path.is_dir():
|
||||
console.print(f"[red]Error: Not a directory: {path}[/red]")
|
||||
return {"status": "error", "message": "Not a directory"}
|
||||
|
||||
# Require confirmation for sensitive operations
|
||||
if not confirm:
|
||||
console.print("[yellow]⚠ Use --confirm to proceed[/yellow]")
|
||||
return {"status": "cancelled"}
|
||||
|
||||
# Perform deployment
|
||||
console.print(f"[green]Deploying from {path}...[/green]")
|
||||
return {"status": "success"}
|
||||
```
|
||||
|
||||
## Pattern 5: Chaining Commands
|
||||
|
||||
Create chainable command patterns:
|
||||
|
||||
```python
|
||||
class Builder:
|
||||
"""Build pipeline with chaining"""
|
||||
|
||||
def __init__(self):
|
||||
self.steps = []
|
||||
|
||||
def clean(self):
|
||||
"""Add clean step"""
|
||||
self.steps.append("clean")
|
||||
return self # Return self for chaining
|
||||
|
||||
def build(self):
|
||||
"""Add build step"""
|
||||
self.steps.append("build")
|
||||
return self
|
||||
|
||||
def test(self):
|
||||
"""Add test step"""
|
||||
self.steps.append("test")
|
||||
return self
|
||||
|
||||
def execute(self):
|
||||
"""Execute pipeline"""
|
||||
for step in self.steps:
|
||||
console.print(f"[cyan]Running: {step}[/cyan]")
|
||||
return {"steps": self.steps}
|
||||
|
||||
class MyCLI:
|
||||
def __init__(self):
|
||||
self.builder = Builder()
|
||||
|
||||
# Usage:
|
||||
# python cli.py builder clean
|
||||
# python cli.py builder build
|
||||
# python cli.py builder clean build test execute
|
||||
```
|
||||
|
||||
## Pattern 6: Context Managers for Resources
|
||||
|
||||
Use context managers for resource handling:
|
||||
|
||||
```python
|
||||
from contextlib import contextmanager
|
||||
|
||||
class MyCLI:
|
||||
@contextmanager
|
||||
def _deployment_context(self, env):
|
||||
"""Context manager for deployments"""
|
||||
console.print(f"[cyan]Starting deployment to {env}...[/cyan]")
|
||||
try:
|
||||
yield
|
||||
console.print("[green]✓[/green] Deployment successful")
|
||||
except Exception as e:
|
||||
console.print(f"[red]✗ Deployment failed: {e}[/red]")
|
||||
raise
|
||||
finally:
|
||||
console.print("[dim]Cleanup complete[/dim]")
|
||||
|
||||
def deploy(self, env='staging'):
|
||||
"""Deploy with context management"""
|
||||
with self._deployment_context(env):
|
||||
# Deployment logic here
|
||||
console.print(" [dim]Building...[/dim]")
|
||||
console.print(" [dim]Uploading...[/dim]")
|
||||
```
|
||||
|
||||
## Pattern 7: Plugin Architecture
|
||||
|
||||
Create extensible CLI with plugins:
|
||||
|
||||
```python
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import List
|
||||
|
||||
class Plugin(ABC):
|
||||
"""Base plugin class"""
|
||||
|
||||
@abstractmethod
|
||||
def execute(self, *args, **kwargs):
|
||||
"""Execute plugin"""
|
||||
pass
|
||||
|
||||
class DatabasePlugin(Plugin):
|
||||
"""Database operations plugin"""
|
||||
|
||||
def execute(self, operation):
|
||||
console.print(f"[cyan]Database: {operation}[/cyan]")
|
||||
|
||||
class CachePlugin(Plugin):
|
||||
"""Cache operations plugin"""
|
||||
|
||||
def execute(self, operation):
|
||||
console.print(f"[yellow]Cache: {operation}[/yellow]")
|
||||
|
||||
class MyCLI:
|
||||
def __init__(self):
|
||||
self.plugins: List[Plugin] = [
|
||||
DatabasePlugin(),
|
||||
CachePlugin()
|
||||
]
|
||||
|
||||
def run_plugins(self, operation):
|
||||
"""Execute operation on all plugins
|
||||
|
||||
Args:
|
||||
operation: Operation to run
|
||||
"""
|
||||
for plugin in self.plugins:
|
||||
plugin.execute(operation)
|
||||
```
|
||||
|
||||
## Pattern 8: Async Operations
|
||||
|
||||
Handle async operations in Fire CLI:
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from typing import List
|
||||
|
||||
class MyCLI:
|
||||
def fetch(self, urls: List[str]):
|
||||
"""Fetch multiple URLs concurrently
|
||||
|
||||
Args:
|
||||
urls: List of URLs to fetch
|
||||
"""
|
||||
async def fetch_all():
|
||||
tasks = [self._fetch_one(url) for url in urls]
|
||||
return await asyncio.gather(*tasks)
|
||||
|
||||
results = asyncio.run(fetch_all())
|
||||
return {"fetched": len(results)}
|
||||
|
||||
async def _fetch_one(self, url):
|
||||
"""Fetch single URL"""
|
||||
# Simulated async fetch
|
||||
await asyncio.sleep(0.1)
|
||||
return url
|
||||
|
||||
# Usage:
|
||||
# python cli.py fetch https://example.com https://google.com
|
||||
```
|
||||
|
||||
## Pattern 9: Dry Run Mode
|
||||
|
||||
Implement dry-run capability:
|
||||
|
||||
```python
|
||||
class MyCLI:
|
||||
def __init__(self):
|
||||
self.dry_run = False
|
||||
|
||||
def deploy(self, env, dry_run=False):
|
||||
"""Deploy to environment
|
||||
|
||||
Args:
|
||||
env: Target environment
|
||||
dry_run: Show what would happen without executing
|
||||
"""
|
||||
self.dry_run = dry_run
|
||||
|
||||
if self.dry_run:
|
||||
console.print("[yellow]DRY RUN MODE[/yellow]")
|
||||
|
||||
self._execute("Build project", lambda: self._build())
|
||||
self._execute("Run tests", lambda: self._test())
|
||||
self._execute("Upload files", lambda: self._upload(env))
|
||||
|
||||
def _execute(self, description, action):
|
||||
"""Execute or simulate action"""
|
||||
if self.dry_run:
|
||||
console.print(f"[dim]Would: {description}[/dim]")
|
||||
else:
|
||||
console.print(f"[cyan]{description}...[/cyan]")
|
||||
action()
|
||||
|
||||
def _build(self):
|
||||
pass # Build logic
|
||||
|
||||
def _test(self):
|
||||
pass # Test logic
|
||||
|
||||
def _upload(self, env):
|
||||
pass # Upload logic
|
||||
```
|
||||
|
||||
## Pattern 10: Logging Integration
|
||||
|
||||
Integrate structured logging:
|
||||
|
||||
```python
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
class MyCLI:
|
||||
def __init__(self):
|
||||
self._setup_logging()
|
||||
|
||||
def _setup_logging(self):
|
||||
"""Configure logging"""
|
||||
log_file = Path.home() / ".mycli" / "cli.log"
|
||||
log_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.FileHandler(log_file),
|
||||
logging.StreamHandler()
|
||||
]
|
||||
)
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
||||
def deploy(self, env):
|
||||
"""Deploy with logging"""
|
||||
self.logger.info(f"Starting deployment to {env}")
|
||||
try:
|
||||
console.print(f"[cyan]Deploying to {env}...[/cyan]")
|
||||
# Deployment logic
|
||||
self.logger.info("Deployment successful")
|
||||
console.print("[green]✓[/green] Deployed!")
|
||||
except Exception as e:
|
||||
self.logger.error(f"Deployment failed: {e}")
|
||||
console.print(f"[red]✗ Error: {e}[/red]")
|
||||
raise
|
||||
```
|
||||
|
||||
## Pattern 11: Interactive Confirmation
|
||||
|
||||
Add interactive prompts:
|
||||
|
||||
```python
|
||||
class MyCLI:
|
||||
def delete(self, resource, force=False):
|
||||
"""Delete resource with confirmation
|
||||
|
||||
Args:
|
||||
resource: Resource to delete
|
||||
force: Skip confirmation
|
||||
"""
|
||||
if not force:
|
||||
console.print(f"[yellow]⚠ Delete {resource}?[/yellow]")
|
||||
console.print("[dim]Use --force to skip this prompt[/dim]")
|
||||
return
|
||||
|
||||
console.print(f"[red]Deleting {resource}...[/red]")
|
||||
# Delete logic here
|
||||
console.print("[green]✓[/green] Deleted")
|
||||
```
|
||||
|
||||
## Best Practices Summary
|
||||
|
||||
1. **Error Handling**: Always validate inputs and handle errors gracefully
|
||||
2. **Confirmation**: Require confirmation for destructive operations
|
||||
3. **Dry Run**: Implement dry-run mode for risky operations
|
||||
4. **Logging**: Log important actions to file for audit trail
|
||||
5. **Type Hints**: Use type hints for better IDE support
|
||||
6. **Properties**: Use properties for read-only values
|
||||
7. **Context Managers**: Use context managers for resource cleanup
|
||||
8. **Enums**: Use Enums for constrained choices
|
||||
9. **Async**: Use asyncio for concurrent operations
|
||||
10. **Plugins**: Design for extensibility with plugin architecture
|
||||
172
skills/fire-patterns/examples/basic-cli.md
Normal file
172
skills/fire-patterns/examples/basic-cli.md
Normal file
@@ -0,0 +1,172 @@
|
||||
# Basic Fire CLI Example
|
||||
|
||||
This example demonstrates creating a simple Fire CLI with basic commands.
|
||||
|
||||
## Generate Basic CLI
|
||||
|
||||
```bash
|
||||
./scripts/generate-fire-cli.sh \
|
||||
--name "TaskManager" \
|
||||
--description "Simple task management CLI" \
|
||||
--template basic \
|
||||
--output task_manager.py
|
||||
```
|
||||
|
||||
## Generated CLI Structure
|
||||
|
||||
```python
|
||||
import fire
|
||||
from rich.console import Console
|
||||
|
||||
console = Console()
|
||||
|
||||
class TaskManager:
|
||||
"""Simple task management CLI"""
|
||||
|
||||
def __init__(self):
|
||||
self.version = "1.0.0"
|
||||
self.verbose = False
|
||||
|
||||
def init(self, name='my-project'):
|
||||
"""Initialize a new project
|
||||
|
||||
Args:
|
||||
name: Project name (default: my-project)
|
||||
"""
|
||||
console.print(f"[green]✓[/green] Initializing project: {name}")
|
||||
return {"status": "success", "project": name}
|
||||
|
||||
def build(self, verbose=False):
|
||||
"""Build the project
|
||||
|
||||
Args:
|
||||
verbose: Enable verbose output (default: False)
|
||||
"""
|
||||
self.verbose = verbose
|
||||
if self.verbose:
|
||||
console.print("[dim]Verbose mode enabled[/dim]")
|
||||
|
||||
console.print("[cyan]Building project...[/cyan]")
|
||||
console.print("[green]✓[/green] Build complete!")
|
||||
|
||||
if __name__ == '__main__':
|
||||
fire.Fire(TaskManager)
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Display Help
|
||||
|
||||
```bash
|
||||
python task_manager.py --help
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
NAME
|
||||
task_manager.py
|
||||
|
||||
SYNOPSIS
|
||||
task_manager.py COMMAND
|
||||
|
||||
COMMANDS
|
||||
COMMAND is one of the following:
|
||||
|
||||
init
|
||||
Initialize a new project
|
||||
|
||||
build
|
||||
Build the project
|
||||
|
||||
version_info
|
||||
Display version information
|
||||
```
|
||||
|
||||
### Initialize Project
|
||||
|
||||
```bash
|
||||
python task_manager.py init
|
||||
# Uses default name 'my-project'
|
||||
|
||||
python task_manager.py init --name=my-app
|
||||
# Custom project name
|
||||
```
|
||||
|
||||
### Build with Verbose Mode
|
||||
|
||||
```bash
|
||||
python task_manager.py build --verbose
|
||||
```
|
||||
|
||||
### Get Version Information
|
||||
|
||||
```bash
|
||||
python task_manager.py version-info
|
||||
```
|
||||
|
||||
## Key Features
|
||||
|
||||
1. **Automatic Help Generation**: Fire generates help text from docstrings
|
||||
2. **Type Conversion**: Fire automatically converts string arguments to correct types
|
||||
3. **Default Values**: Parameter defaults become CLI defaults
|
||||
4. **Boolean Flags**: `verbose=False` becomes `--verbose` flag
|
||||
5. **Rich Output**: Integration with rich console for colored output
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Boolean Flags
|
||||
|
||||
```python
|
||||
def deploy(self, force=False, dry_run=False):
|
||||
"""Deploy application
|
||||
|
||||
Args:
|
||||
force: Force deployment
|
||||
dry_run: Perform dry run only
|
||||
"""
|
||||
pass
|
||||
|
||||
# Usage:
|
||||
# python cli.py deploy --force
|
||||
# python cli.py deploy --dry-run
|
||||
# python cli.py deploy --noforce # Explicit False
|
||||
```
|
||||
|
||||
### Required vs Optional Arguments
|
||||
|
||||
```python
|
||||
def create(self, name, template='default'):
|
||||
"""Create resource
|
||||
|
||||
Args:
|
||||
name: Resource name (required)
|
||||
template: Template to use (optional)
|
||||
"""
|
||||
pass
|
||||
|
||||
# Usage:
|
||||
# python cli.py create my-resource
|
||||
# python cli.py create my-resource --template=advanced
|
||||
```
|
||||
|
||||
### Returning Values
|
||||
|
||||
```python
|
||||
def status(self):
|
||||
"""Get status"""
|
||||
return {
|
||||
"running": True,
|
||||
"version": "1.0.0",
|
||||
"uptime": "24h"
|
||||
}
|
||||
|
||||
# Fire will display the returned dict
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Add more commands as methods
|
||||
2. Use rich console for better output
|
||||
3. Add configuration management
|
||||
4. Implement nested classes for command groups
|
||||
5. Add type hints for better IDE support
|
||||
262
skills/fire-patterns/examples/nested-commands.md
Normal file
262
skills/fire-patterns/examples/nested-commands.md
Normal file
@@ -0,0 +1,262 @@
|
||||
# Nested Commands Example
|
||||
|
||||
This example demonstrates using nested classes to organize related commands into groups.
|
||||
|
||||
## Generate Nested CLI
|
||||
|
||||
```bash
|
||||
./scripts/generate-fire-cli.sh \
|
||||
--name "DeployTool" \
|
||||
--description "Deployment management tool" \
|
||||
--template nested \
|
||||
--output deploy_tool.py
|
||||
```
|
||||
|
||||
## Command Structure
|
||||
|
||||
```
|
||||
deploy_tool.py
|
||||
├── config # Configuration group
|
||||
│ ├── get # Get config value
|
||||
│ ├── set # Set config value
|
||||
│ ├── list # List all config
|
||||
│ └── reset # Reset config
|
||||
├── resources # Resources group
|
||||
│ ├── create # Create resource
|
||||
│ ├── delete # Delete resource
|
||||
│ └── list # List resources
|
||||
└── info # Display info
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Configuration Management
|
||||
|
||||
```bash
|
||||
# Set configuration value
|
||||
python deploy_tool.py config set api_key abc123
|
||||
|
||||
# Get configuration value
|
||||
python deploy_tool.py config get api_key
|
||||
# Output: api_key: abc123
|
||||
|
||||
# List all configuration
|
||||
python deploy_tool.py config list
|
||||
# Output:
|
||||
# Configuration:
|
||||
# api_key: abc123
|
||||
# endpoint: https://api.example.com
|
||||
|
||||
# Reset configuration
|
||||
python deploy_tool.py config reset --confirm
|
||||
```
|
||||
|
||||
### Resource Management
|
||||
|
||||
```bash
|
||||
# Create resource with default template
|
||||
python deploy_tool.py resources create my-resource
|
||||
|
||||
# Create resource with custom template
|
||||
python deploy_tool.py resources create my-resource --template=advanced
|
||||
|
||||
# List all resources
|
||||
python deploy_tool.py resources list
|
||||
# Output:
|
||||
# Resources:
|
||||
# • item1
|
||||
# • item2
|
||||
# • item3
|
||||
|
||||
# Delete resource (requires confirmation)
|
||||
python deploy_tool.py resources delete my-resource
|
||||
# Output: ⚠ Use --confirm to delete resource
|
||||
|
||||
python deploy_tool.py resources delete my-resource --confirm
|
||||
# Output: ✓ resource deleted
|
||||
```
|
||||
|
||||
### Display Information
|
||||
|
||||
```bash
|
||||
python deploy_tool.py info
|
||||
# Output:
|
||||
# DeployTool v1.0.0
|
||||
# Config file: /home/user/.deploytool/config.json
|
||||
```
|
||||
|
||||
## Implementation Pattern
|
||||
|
||||
### Main CLI Class
|
||||
|
||||
```python
|
||||
class DeployTool:
|
||||
"""Main CLI application"""
|
||||
|
||||
def __init__(self):
|
||||
self.version = "1.0.0"
|
||||
self.config_file = Path.home() / ".deploytool" / "config.json"
|
||||
# Initialize nested command groups
|
||||
self.config = self.Config(self)
|
||||
self.resources = self.Resources()
|
||||
|
||||
def info(self):
|
||||
"""Display CLI information"""
|
||||
console.print(f"[bold]DeployTool[/bold] v{self.version}")
|
||||
return {"version": self.version}
|
||||
```
|
||||
|
||||
### Nested Command Group
|
||||
|
||||
```python
|
||||
class Config:
|
||||
"""Configuration management commands"""
|
||||
|
||||
def __init__(self, parent):
|
||||
self.parent = parent # Access to main CLI instance
|
||||
|
||||
def get(self, key):
|
||||
"""Get configuration value
|
||||
|
||||
Args:
|
||||
key: Configuration key to retrieve
|
||||
"""
|
||||
config = self._load_config()
|
||||
value = config.get(key)
|
||||
console.print(f"[blue]{key}[/blue]: {value}")
|
||||
return value
|
||||
|
||||
def set(self, key, value):
|
||||
"""Set configuration value
|
||||
|
||||
Args:
|
||||
key: Configuration key to set
|
||||
value: Configuration value
|
||||
"""
|
||||
config = self._load_config()
|
||||
config[key] = value
|
||||
self._save_config(config)
|
||||
console.print(f"[green]✓[/green] Set {key} = {value}")
|
||||
|
||||
def _load_config(self):
|
||||
"""Private helper method (not exposed as command)"""
|
||||
if not self.parent.config_file.exists():
|
||||
return {}
|
||||
return json.loads(self.parent.config_file.read_text())
|
||||
|
||||
def _save_config(self, config):
|
||||
"""Private helper method (not exposed as command)"""
|
||||
self.parent.config_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
self.parent.config_file.write_text(json.dumps(config, indent=2))
|
||||
```
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### Parent Access
|
||||
|
||||
Nested classes can access the parent CLI instance:
|
||||
|
||||
```python
|
||||
class Config:
|
||||
def __init__(self, parent):
|
||||
self.parent = parent # Store parent reference
|
||||
|
||||
def some_command(self):
|
||||
# Access parent properties
|
||||
version = self.parent.version
|
||||
config_file = self.parent.config_file
|
||||
```
|
||||
|
||||
### Private Methods
|
||||
|
||||
Methods starting with `_` are not exposed as CLI commands:
|
||||
|
||||
```python
|
||||
def list(self):
|
||||
"""Public command - accessible via CLI"""
|
||||
pass
|
||||
|
||||
def _load_config(self):
|
||||
"""Private helper - not accessible via CLI"""
|
||||
pass
|
||||
```
|
||||
|
||||
### Multiple Nesting Levels
|
||||
|
||||
You can nest command groups multiple levels deep:
|
||||
|
||||
```python
|
||||
class CLI:
|
||||
class Database:
|
||||
"""Database commands"""
|
||||
|
||||
class Migration:
|
||||
"""Migration subcommands"""
|
||||
|
||||
def up(self):
|
||||
"""Run migrations up"""
|
||||
pass
|
||||
|
||||
def down(self):
|
||||
"""Run migrations down"""
|
||||
pass
|
||||
|
||||
# Usage:
|
||||
# python cli.py database migration up
|
||||
# python cli.py database migration down
|
||||
```
|
||||
|
||||
## Help Navigation
|
||||
|
||||
### Top-Level Help
|
||||
|
||||
```bash
|
||||
python deploy_tool.py --help
|
||||
```
|
||||
|
||||
Shows all command groups and top-level commands.
|
||||
|
||||
### Group-Level Help
|
||||
|
||||
```bash
|
||||
python deploy_tool.py config --help
|
||||
```
|
||||
|
||||
Shows all commands in the `config` group.
|
||||
|
||||
### Command-Level Help
|
||||
|
||||
```bash
|
||||
python deploy_tool.py config set --help
|
||||
```
|
||||
|
||||
Shows help for specific command including arguments.
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Logical Grouping**: Group related commands together
|
||||
2. **Clear Names**: Use descriptive names for groups and commands
|
||||
3. **Parent Access**: Use parent reference to share state
|
||||
4. **Private Helpers**: Use `_` prefix for helper methods
|
||||
5. **Comprehensive Docs**: Document each command group and command
|
||||
6. **Shallow Nesting**: Keep nesting to 2-3 levels maximum
|
||||
|
||||
## Advanced Pattern: Shared Context
|
||||
|
||||
```python
|
||||
class CLI:
|
||||
def __init__(self):
|
||||
self.context = {"verbose": False, "config": {}}
|
||||
|
||||
class Commands:
|
||||
def __init__(self, parent):
|
||||
self.parent = parent
|
||||
|
||||
def run(self, verbose=False):
|
||||
"""Run command"""
|
||||
self.parent.context["verbose"] = verbose
|
||||
if verbose:
|
||||
console.print("[dim]Verbose mode enabled[/dim]")
|
||||
```
|
||||
|
||||
This allows sharing state across command groups through the parent context.
|
||||
322
skills/fire-patterns/examples/rich-integration.md
Normal file
322
skills/fire-patterns/examples/rich-integration.md
Normal file
@@ -0,0 +1,322 @@
|
||||
# Rich Console Integration Example
|
||||
|
||||
This example shows how to integrate Fire CLI with Rich library for beautiful terminal output.
|
||||
|
||||
## Generate Rich CLI
|
||||
|
||||
```bash
|
||||
./scripts/generate-fire-cli.sh \
|
||||
--name "Monitor" \
|
||||
--description "System monitoring CLI" \
|
||||
--template rich \
|
||||
--output monitor.py
|
||||
```
|
||||
|
||||
## Rich Features
|
||||
|
||||
### Tables
|
||||
|
||||
Display data in formatted tables:
|
||||
|
||||
```python
|
||||
from rich.table import Table
|
||||
|
||||
def list_items(self):
|
||||
"""List items with table formatting"""
|
||||
items = [
|
||||
{"id": 1, "name": "Service A", "status": "active", "uptime": "99.9%"},
|
||||
{"id": 2, "name": "Service B", "status": "down", "uptime": "0%"},
|
||||
{"id": 3, "name": "Service C", "status": "pending", "uptime": "N/A"},
|
||||
]
|
||||
|
||||
table = Table(title="System Services", show_header=True, header_style="bold magenta")
|
||||
table.add_column("ID", style="cyan", width=6)
|
||||
table.add_column("Name", style="green")
|
||||
table.add_column("Status", style="yellow")
|
||||
table.add_column("Uptime", justify="right", style="blue")
|
||||
|
||||
for item in items:
|
||||
# Dynamic styling based on status
|
||||
status_style = {
|
||||
"active": "green",
|
||||
"down": "red",
|
||||
"pending": "yellow"
|
||||
}.get(item['status'], "white")
|
||||
|
||||
table.add_row(
|
||||
str(item['id']),
|
||||
item['name'],
|
||||
f"[{status_style}]{item['status']}[/{status_style}]",
|
||||
item['uptime']
|
||||
)
|
||||
|
||||
console.print(table)
|
||||
return items
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
python monitor.py list-items
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
System Services
|
||||
┏━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┓
|
||||
┃ ID ┃ Name ┃ Status ┃ Uptime ┃
|
||||
┡━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━┩
|
||||
│ 1 │ Service A │ active │ 99.9% │
|
||||
│ 2 │ Service B │ down │ 0% │
|
||||
│ 3 │ Service C │ pending │ N/A │
|
||||
└──────┴───────────┴─────────┴────────┘
|
||||
```
|
||||
|
||||
### Progress Bars
|
||||
|
||||
Show progress for long-running operations:
|
||||
|
||||
```python
|
||||
from rich.progress import track
|
||||
import time
|
||||
|
||||
def process(self, count=100):
|
||||
"""Process items with progress bar
|
||||
|
||||
Args:
|
||||
count: Number of items to process
|
||||
"""
|
||||
console.print("[cyan]Starting processing...[/cyan]")
|
||||
|
||||
results = []
|
||||
for i in track(range(count), description="Processing..."):
|
||||
time.sleep(0.01) # Simulate work
|
||||
results.append(i)
|
||||
|
||||
console.print("[green]✓[/green] Processing complete!")
|
||||
return {"processed": len(results)}
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
python monitor.py process --count=50
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
Starting processing...
|
||||
Processing... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00
|
||||
✓ Processing complete!
|
||||
```
|
||||
|
||||
### Panels
|
||||
|
||||
Display information in bordered panels:
|
||||
|
||||
```python
|
||||
from rich.panel import Panel
|
||||
|
||||
def status(self):
|
||||
"""Display system status"""
|
||||
panel = Panel(
|
||||
"[bold green]System Running[/bold green]\n"
|
||||
"Version: 1.0.0\n"
|
||||
"Uptime: 24 hours\n"
|
||||
"Load: [yellow]0.45[/yellow]\n"
|
||||
"Memory: [blue]45%[/blue]",
|
||||
title="System Status",
|
||||
border_style="green"
|
||||
)
|
||||
console.print(panel)
|
||||
|
||||
return {
|
||||
"status": "running",
|
||||
"version": "1.0.0",
|
||||
"uptime": "24h"
|
||||
}
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
python monitor.py status
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
╭─────── System Status ────────╮
|
||||
│ System Running │
|
||||
│ Version: 1.0.0 │
|
||||
│ Uptime: 24 hours │
|
||||
│ Load: 0.45 │
|
||||
│ Memory: 45% │
|
||||
╰──────────────────────────────╯
|
||||
```
|
||||
|
||||
### Trees
|
||||
|
||||
Display hierarchical data:
|
||||
|
||||
```python
|
||||
from rich.tree import Tree
|
||||
|
||||
def show_structure(self):
|
||||
"""Display project structure"""
|
||||
tree = Tree("📁 Project Root")
|
||||
|
||||
src = tree.add("📁 src")
|
||||
src.add("📄 main.py")
|
||||
src.add("📄 config.py")
|
||||
|
||||
tests = tree.add("📁 tests")
|
||||
tests.add("📄 test_main.py")
|
||||
tests.add("📄 test_config.py")
|
||||
|
||||
tree.add("📄 README.md")
|
||||
tree.add("📄 requirements.txt")
|
||||
|
||||
console.print(tree)
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
python monitor.py show-structure
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
📁 Project Root
|
||||
├── 📁 src
|
||||
│ ├── 📄 main.py
|
||||
│ └── 📄 config.py
|
||||
├── 📁 tests
|
||||
│ ├── 📄 test_main.py
|
||||
│ └── 📄 test_config.py
|
||||
├── 📄 README.md
|
||||
└── 📄 requirements.txt
|
||||
```
|
||||
|
||||
### Styled Output
|
||||
|
||||
Use rich markup for styled text:
|
||||
|
||||
```python
|
||||
def deploy(self, environment):
|
||||
"""Deploy to environment
|
||||
|
||||
Args:
|
||||
environment: Target environment
|
||||
"""
|
||||
console.print(f"[bold cyan]Deploying to {environment}...[/bold cyan]")
|
||||
console.print("[dim]Step 1: Building...[/dim]")
|
||||
console.print("[dim]Step 2: Testing...[/dim]")
|
||||
console.print("[dim]Step 3: Uploading...[/dim]")
|
||||
console.print("[green]✓[/green] Deployment complete!")
|
||||
|
||||
# Error example
|
||||
if environment == "production":
|
||||
console.print("[red]⚠ Production deployment requires approval[/red]")
|
||||
```
|
||||
|
||||
### Color Palette
|
||||
|
||||
Common rich colors and styles:
|
||||
|
||||
```python
|
||||
# Colors
|
||||
console.print("[red]Error message[/red]")
|
||||
console.print("[green]Success message[/green]")
|
||||
console.print("[yellow]Warning message[/yellow]")
|
||||
console.print("[blue]Info message[/blue]")
|
||||
console.print("[cyan]Action message[/cyan]")
|
||||
console.print("[magenta]Highlight[/magenta]")
|
||||
|
||||
# Styles
|
||||
console.print("[bold]Bold text[/bold]")
|
||||
console.print("[dim]Dimmed text[/dim]")
|
||||
console.print("[italic]Italic text[/italic]")
|
||||
console.print("[underline]Underlined text[/underline]")
|
||||
|
||||
# Combinations
|
||||
console.print("[bold red]Bold red text[/bold red]")
|
||||
console.print("[dim yellow]Dimmed yellow text[/dim yellow]")
|
||||
|
||||
# Emojis
|
||||
console.print("✓ Success")
|
||||
console.print("✗ Failure")
|
||||
console.print("⚠ Warning")
|
||||
console.print("ℹ Info")
|
||||
console.print("→ Next step")
|
||||
console.print("🚀 Deploy")
|
||||
console.print("📦 Package")
|
||||
```
|
||||
|
||||
## Complete Rich CLI Example
|
||||
|
||||
```python
|
||||
import fire
|
||||
from rich.console import Console
|
||||
from rich.table import Table
|
||||
from rich.panel import Panel
|
||||
from rich.progress import track
|
||||
import time
|
||||
|
||||
console = Console()
|
||||
|
||||
class Monitor:
|
||||
"""System monitoring CLI with rich output"""
|
||||
|
||||
def __init__(self):
|
||||
self.version = "1.0.0"
|
||||
|
||||
def status(self):
|
||||
"""Display comprehensive status"""
|
||||
# Header panel
|
||||
console.print(Panel(
|
||||
"[bold green]System Online[/bold green]",
|
||||
title="Monitor Status",
|
||||
border_style="green"
|
||||
))
|
||||
|
||||
# Services table
|
||||
table = Table(title="Services")
|
||||
table.add_column("Service", style="cyan")
|
||||
table.add_column("Status", style="green")
|
||||
table.add_column("Load", justify="right")
|
||||
|
||||
table.add_row("API", "[green]●[/green] Running", "45%")
|
||||
table.add_row("DB", "[green]●[/green] Running", "23%")
|
||||
table.add_row("Cache", "[yellow]●[/yellow] Degraded", "78%")
|
||||
|
||||
console.print(table)
|
||||
|
||||
def deploy(self, env='staging'):
|
||||
"""Deploy with visual feedback"""
|
||||
console.print(Panel(
|
||||
f"[bold]Deploying to {env}[/bold]",
|
||||
border_style="cyan"
|
||||
))
|
||||
|
||||
steps = ["Build", "Test", "Upload", "Verify"]
|
||||
for step in track(steps, description="Deploying..."):
|
||||
time.sleep(0.5)
|
||||
|
||||
console.print("[green]✓[/green] Deployment successful!")
|
||||
|
||||
if __name__ == '__main__':
|
||||
fire.Fire(Monitor)
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install fire rich
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use Console Instance**: Create one `Console()` instance and reuse it
|
||||
2. **Consistent Colors**: Use consistent colors for similar message types
|
||||
3. **Progress for Long Tasks**: Always show progress for operations >1 second
|
||||
4. **Tables for Lists**: Use tables instead of plain text for structured data
|
||||
5. **Panels for Sections**: Use panels to separate different output sections
|
||||
6. **Emojis Sparingly**: Use emojis to enhance, not clutter
|
||||
7. **Test in Different Terminals**: Rich output varies by terminal capabilities
|
||||
Reference in New Issue
Block a user