Initial commit
This commit is contained in:
233
skills/typer-patterns/templates/advanced-validation.py
Normal file
233
skills/typer-patterns/templates/advanced-validation.py
Normal file
@@ -0,0 +1,233 @@
|
||||
"""Advanced validation and callbacks template.
|
||||
|
||||
This template demonstrates:
|
||||
- Custom validators with callbacks
|
||||
- Complex validation logic
|
||||
- Interdependent parameter validation
|
||||
- Rich error messages
|
||||
"""
|
||||
|
||||
import typer
|
||||
from typing import Optional
|
||||
from pathlib import Path
|
||||
import re
|
||||
|
||||
|
||||
app = typer.Typer()
|
||||
|
||||
|
||||
# Custom validators
|
||||
def validate_email(value: str) -> str:
|
||||
"""Validate email format."""
|
||||
pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
|
||||
if not re.match(pattern, value):
|
||||
raise typer.BadParameter("Invalid email format")
|
||||
return value
|
||||
|
||||
|
||||
def validate_port(value: int) -> int:
|
||||
"""Validate port number range."""
|
||||
if not 1024 <= value <= 65535:
|
||||
raise typer.BadParameter("Port must be between 1024-65535")
|
||||
return value
|
||||
|
||||
|
||||
def validate_path_exists(value: Path) -> Path:
|
||||
"""Validate that path exists."""
|
||||
if not value.exists():
|
||||
raise typer.BadParameter(f"Path does not exist: {value}")
|
||||
return value
|
||||
|
||||
|
||||
def validate_percentage(value: float) -> float:
|
||||
"""Validate percentage range."""
|
||||
if not 0.0 <= value <= 100.0:
|
||||
raise typer.BadParameter("Percentage must be between 0-100")
|
||||
return value
|
||||
|
||||
|
||||
def validate_url(value: str) -> str:
|
||||
"""Validate URL format."""
|
||||
pattern = r"^https?://[^\s/$.?#].[^\s]*$"
|
||||
if not re.match(pattern, value):
|
||||
raise typer.BadParameter("Invalid URL format (must start with http:// or https://)")
|
||||
return value
|
||||
|
||||
|
||||
# Context manager for complex validation
|
||||
class ValidationContext:
|
||||
"""Context for cross-parameter validation."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.params: dict = {}
|
||||
|
||||
def add(self, key: str, value: any) -> None:
|
||||
"""Add parameter to context."""
|
||||
self.params[key] = value
|
||||
|
||||
def validate_dependencies(self) -> None:
|
||||
"""Validate parameter dependencies."""
|
||||
# Example: if ssl is enabled, cert and key must be provided
|
||||
if self.params.get("ssl") and not (
|
||||
self.params.get("cert") and self.params.get("key")
|
||||
):
|
||||
raise typer.BadParameter("SSL requires both --cert and --key")
|
||||
|
||||
|
||||
# Global validation context
|
||||
validation_context = ValidationContext()
|
||||
|
||||
|
||||
@app.command()
|
||||
def server(
|
||||
host: str = typer.Option(
|
||||
"127.0.0.1",
|
||||
"--host",
|
||||
"-h",
|
||||
help="Server host",
|
||||
),
|
||||
port: int = typer.Option(
|
||||
8000,
|
||||
"--port",
|
||||
"-p",
|
||||
help="Server port",
|
||||
callback=lambda _, value: validate_port(value),
|
||||
),
|
||||
ssl: bool = typer.Option(
|
||||
False,
|
||||
"--ssl",
|
||||
help="Enable SSL/TLS",
|
||||
),
|
||||
cert: Optional[Path] = typer.Option(
|
||||
None,
|
||||
"--cert",
|
||||
help="SSL certificate file",
|
||||
callback=lambda _, value: validate_path_exists(value) if value else None,
|
||||
),
|
||||
key: Optional[Path] = typer.Option(
|
||||
None,
|
||||
"--key",
|
||||
help="SSL private key file",
|
||||
callback=lambda _, value: validate_path_exists(value) if value else None,
|
||||
),
|
||||
) -> None:
|
||||
"""Start server with validated parameters.
|
||||
|
||||
Example:
|
||||
$ python cli.py server --port 8443 --ssl --cert cert.pem --key key.pem
|
||||
"""
|
||||
# Store params for cross-validation
|
||||
validation_context.add("ssl", ssl)
|
||||
validation_context.add("cert", cert)
|
||||
validation_context.add("key", key)
|
||||
|
||||
# Validate dependencies
|
||||
try:
|
||||
validation_context.validate_dependencies()
|
||||
except typer.BadParameter as e:
|
||||
typer.secho(f"✗ Validation error: {e}", fg=typer.colors.RED, err=True)
|
||||
raise typer.Exit(1)
|
||||
|
||||
# Start server
|
||||
protocol = "https" if ssl else "http"
|
||||
typer.echo(f"Starting server at {protocol}://{host}:{port}")
|
||||
|
||||
|
||||
@app.command()
|
||||
def user_create(
|
||||
username: str = typer.Argument(..., help="Username (alphanumeric only)"),
|
||||
email: str = typer.Option(
|
||||
...,
|
||||
"--email",
|
||||
"-e",
|
||||
help="User email",
|
||||
callback=lambda _, value: validate_email(value),
|
||||
),
|
||||
age: Optional[int] = typer.Option(
|
||||
None,
|
||||
"--age",
|
||||
help="User age",
|
||||
min=13,
|
||||
max=120,
|
||||
),
|
||||
) -> None:
|
||||
"""Create user with validated inputs.
|
||||
|
||||
Example:
|
||||
$ python cli.py user-create john --email john@example.com --age 25
|
||||
"""
|
||||
# Additional username validation
|
||||
if not username.isalnum():
|
||||
typer.secho(
|
||||
"✗ Username must be alphanumeric", fg=typer.colors.RED, err=True
|
||||
)
|
||||
raise typer.Exit(1)
|
||||
|
||||
typer.secho(f"✓ User created: {username}", fg=typer.colors.GREEN)
|
||||
|
||||
|
||||
@app.command()
|
||||
def deploy(
|
||||
url: str = typer.Option(
|
||||
...,
|
||||
"--url",
|
||||
help="Deployment URL",
|
||||
callback=lambda _, value: validate_url(value),
|
||||
),
|
||||
threshold: float = typer.Option(
|
||||
95.0,
|
||||
"--threshold",
|
||||
help="Success threshold percentage",
|
||||
callback=lambda _, value: validate_percentage(value),
|
||||
),
|
||||
rollback_on_error: bool = typer.Option(
|
||||
True, "--rollback/--no-rollback", help="Rollback on error"
|
||||
),
|
||||
) -> None:
|
||||
"""Deploy with validated URL and threshold.
|
||||
|
||||
Example:
|
||||
$ python cli.py deploy --url https://example.com --threshold 99.5
|
||||
"""
|
||||
typer.echo(f"Deploying to: {url}")
|
||||
typer.echo(f"Success threshold: {threshold}%")
|
||||
typer.echo(f"Rollback on error: {rollback_on_error}")
|
||||
|
||||
|
||||
@app.command()
|
||||
def batch_process(
|
||||
input_dir: Path = typer.Argument(
|
||||
...,
|
||||
help="Input directory",
|
||||
callback=lambda _, value: validate_path_exists(value),
|
||||
),
|
||||
pattern: str = typer.Option(
|
||||
"*.txt", "--pattern", "-p", help="File pattern"
|
||||
),
|
||||
workers: int = typer.Option(
|
||||
4,
|
||||
"--workers",
|
||||
"-w",
|
||||
help="Number of worker threads",
|
||||
min=1,
|
||||
max=32,
|
||||
),
|
||||
) -> None:
|
||||
"""Batch process files with validation.
|
||||
|
||||
Example:
|
||||
$ python cli.py batch-process ./data --pattern "*.json" --workers 8
|
||||
"""
|
||||
if not input_dir.is_dir():
|
||||
typer.secho(
|
||||
f"✗ Not a directory: {input_dir}", fg=typer.colors.RED, err=True
|
||||
)
|
||||
raise typer.Exit(1)
|
||||
|
||||
typer.echo(f"Processing files in: {input_dir}")
|
||||
typer.echo(f"Pattern: {pattern}")
|
||||
typer.echo(f"Workers: {workers}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app()
|
||||
68
skills/typer-patterns/templates/basic-typed-command.py
Normal file
68
skills/typer-patterns/templates/basic-typed-command.py
Normal file
@@ -0,0 +1,68 @@
|
||||
"""Basic type-safe Typer command template.
|
||||
|
||||
This template demonstrates modern Typer usage with:
|
||||
- Full type hints on all parameters
|
||||
- Path type for file operations
|
||||
- Optional parameters with defaults
|
||||
- Typed return hints
|
||||
"""
|
||||
|
||||
import typer
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
app = typer.Typer()
|
||||
|
||||
|
||||
@app.command()
|
||||
def process(
|
||||
input_file: Path = typer.Argument(
|
||||
...,
|
||||
help="Input file to process",
|
||||
exists=True,
|
||||
file_okay=True,
|
||||
dir_okay=False,
|
||||
readable=True,
|
||||
),
|
||||
output_file: Optional[Path] = typer.Option(
|
||||
None,
|
||||
"--output",
|
||||
"-o",
|
||||
help="Output file path (optional)",
|
||||
),
|
||||
verbose: bool = typer.Option(
|
||||
False,
|
||||
"--verbose",
|
||||
"-v",
|
||||
help="Enable verbose output",
|
||||
),
|
||||
count: int = typer.Option(
|
||||
10,
|
||||
"--count",
|
||||
"-c",
|
||||
help="Number of items to process",
|
||||
min=1,
|
||||
max=1000,
|
||||
),
|
||||
) -> None:
|
||||
"""Process input file with type-safe parameters.
|
||||
|
||||
Example:
|
||||
$ python cli.py input.txt --output result.txt --verbose --count 50
|
||||
"""
|
||||
if verbose:
|
||||
typer.echo(f"Processing {input_file}")
|
||||
typer.echo(f"Count: {count}")
|
||||
|
||||
# Your processing logic here
|
||||
content = input_file.read_text()
|
||||
|
||||
if output_file:
|
||||
output_file.write_text(content)
|
||||
typer.secho(f"✓ Saved to {output_file}", fg=typer.colors.GREEN)
|
||||
else:
|
||||
typer.echo(content)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app()
|
||||
100
skills/typer-patterns/templates/enum-options.py
Normal file
100
skills/typer-patterns/templates/enum-options.py
Normal file
@@ -0,0 +1,100 @@
|
||||
"""Enum-based options template for Typer.
|
||||
|
||||
This template demonstrates:
|
||||
- Enum usage for constrained choices
|
||||
- Multiple enum types
|
||||
- Enum with autocomplete
|
||||
- Type-safe enum handling
|
||||
"""
|
||||
|
||||
import typer
|
||||
from enum import Enum
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class LogLevel(str, Enum):
|
||||
"""Log level choices."""
|
||||
|
||||
debug = "debug"
|
||||
info = "info"
|
||||
warning = "warning"
|
||||
error = "error"
|
||||
|
||||
|
||||
class OutputFormat(str, Enum):
|
||||
"""Output format choices."""
|
||||
|
||||
json = "json"
|
||||
yaml = "yaml"
|
||||
text = "text"
|
||||
csv = "csv"
|
||||
|
||||
|
||||
class Environment(str, Enum):
|
||||
"""Deployment environment choices."""
|
||||
|
||||
development = "development"
|
||||
staging = "staging"
|
||||
production = "production"
|
||||
|
||||
|
||||
app = typer.Typer()
|
||||
|
||||
|
||||
@app.command()
|
||||
def deploy(
|
||||
environment: Environment = typer.Argument(
|
||||
..., help="Target deployment environment"
|
||||
),
|
||||
format: OutputFormat = typer.Option(
|
||||
OutputFormat.json, "--format", "-f", help="Output format for logs"
|
||||
),
|
||||
log_level: LogLevel = typer.Option(
|
||||
LogLevel.info, "--log-level", "-l", help="Logging level"
|
||||
),
|
||||
force: bool = typer.Option(False, "--force", help="Force deployment"),
|
||||
) -> None:
|
||||
"""Deploy application with enum-based options.
|
||||
|
||||
Example:
|
||||
$ python cli.py production --format yaml --log-level debug
|
||||
"""
|
||||
typer.echo(f"Deploying to: {environment.value}")
|
||||
typer.echo(f"Output format: {format.value}")
|
||||
typer.echo(f"Log level: {log_level.value}")
|
||||
|
||||
if force:
|
||||
typer.secho("⚠ Force deployment enabled", fg=typer.colors.YELLOW)
|
||||
|
||||
# Deployment logic here
|
||||
# The enum values are guaranteed to be valid
|
||||
|
||||
|
||||
@app.command()
|
||||
def export(
|
||||
format: OutputFormat = typer.Argument(
|
||||
OutputFormat.json, help="Export format"
|
||||
),
|
||||
output: Optional[str] = typer.Option(None, "--output", "-o"),
|
||||
) -> None:
|
||||
"""Export data in specified format.
|
||||
|
||||
Example:
|
||||
$ python cli.py export yaml --output data.yaml
|
||||
"""
|
||||
typer.echo(f"Exporting as {format.value}")
|
||||
|
||||
# Export logic based on format
|
||||
match format:
|
||||
case OutputFormat.json:
|
||||
typer.echo("Generating JSON...")
|
||||
case OutputFormat.yaml:
|
||||
typer.echo("Generating YAML...")
|
||||
case OutputFormat.text:
|
||||
typer.echo("Generating plain text...")
|
||||
case OutputFormat.csv:
|
||||
typer.echo("Generating CSV...")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app()
|
||||
164
skills/typer-patterns/templates/sub-app-structure.py
Normal file
164
skills/typer-patterns/templates/sub-app-structure.py
Normal file
@@ -0,0 +1,164 @@
|
||||
"""Sub-application structure template.
|
||||
|
||||
This template demonstrates:
|
||||
- Multiple sub-apps for command organization
|
||||
- Shared context between commands
|
||||
- Hierarchical command structure
|
||||
- Clean separation of concerns
|
||||
"""
|
||||
|
||||
import typer
|
||||
from typing import Optional
|
||||
from pathlib import Path
|
||||
|
||||
# Main application
|
||||
app = typer.Typer(
|
||||
name="mycli",
|
||||
help="Example CLI with sub-applications",
|
||||
add_completion=True,
|
||||
)
|
||||
|
||||
# Database sub-app
|
||||
db_app = typer.Typer(help="Database management commands")
|
||||
app.add_typer(db_app, name="db")
|
||||
|
||||
# Server sub-app
|
||||
server_app = typer.Typer(help="Server management commands")
|
||||
app.add_typer(server_app, name="server")
|
||||
|
||||
# User sub-app
|
||||
user_app = typer.Typer(help="User management commands")
|
||||
app.add_typer(user_app, name="user")
|
||||
|
||||
|
||||
# Main app callback for global options
|
||||
@app.callback()
|
||||
def main(
|
||||
ctx: typer.Context,
|
||||
config: Optional[Path] = typer.Option(
|
||||
None, "--config", "-c", help="Config file path"
|
||||
),
|
||||
verbose: bool = typer.Option(False, "--verbose", "-v"),
|
||||
) -> None:
|
||||
"""Global options for all commands."""
|
||||
# Store in context for sub-commands
|
||||
ctx.obj = {"config": config, "verbose": verbose}
|
||||
|
||||
if verbose:
|
||||
typer.echo(f"Config: {config or 'default'}")
|
||||
|
||||
|
||||
# Database commands
|
||||
@db_app.command("migrate")
|
||||
def db_migrate(
|
||||
ctx: typer.Context,
|
||||
direction: str = typer.Argument("up", help="Migration direction: up/down"),
|
||||
steps: int = typer.Option(1, help="Number of migration steps"),
|
||||
) -> None:
|
||||
"""Run database migrations."""
|
||||
verbose = ctx.obj.get("verbose", False)
|
||||
|
||||
if verbose:
|
||||
typer.echo(f"Running {steps} migration(s) {direction}")
|
||||
|
||||
typer.secho("✓ Migrations complete", fg=typer.colors.GREEN)
|
||||
|
||||
|
||||
@db_app.command("seed")
|
||||
def db_seed(
|
||||
ctx: typer.Context, file: Optional[Path] = typer.Option(None, "--file", "-f")
|
||||
) -> None:
|
||||
"""Seed database with test data."""
|
||||
verbose = ctx.obj.get("verbose", False)
|
||||
|
||||
if verbose:
|
||||
typer.echo(f"Seeding from: {file or 'default seed'}")
|
||||
|
||||
typer.secho("✓ Database seeded", fg=typer.colors.GREEN)
|
||||
|
||||
|
||||
@db_app.command("backup")
|
||||
def db_backup(ctx: typer.Context, output: Path = typer.Argument(...)) -> None:
|
||||
"""Backup database to file."""
|
||||
typer.echo(f"Backing up database to {output}")
|
||||
typer.secho("✓ Backup complete", fg=typer.colors.GREEN)
|
||||
|
||||
|
||||
# Server commands
|
||||
@server_app.command("start")
|
||||
def server_start(
|
||||
ctx: typer.Context,
|
||||
port: int = typer.Option(8000, help="Server port"),
|
||||
host: str = typer.Option("127.0.0.1", help="Server host"),
|
||||
reload: bool = typer.Option(False, "--reload", help="Enable auto-reload"),
|
||||
) -> None:
|
||||
"""Start the application server."""
|
||||
verbose = ctx.obj.get("verbose", False)
|
||||
|
||||
if verbose:
|
||||
typer.echo(f"Starting server on {host}:{port}")
|
||||
|
||||
if reload:
|
||||
typer.echo("Auto-reload enabled")
|
||||
|
||||
typer.secho("✓ Server started", fg=typer.colors.GREEN)
|
||||
|
||||
|
||||
@server_app.command("stop")
|
||||
def server_stop(ctx: typer.Context) -> None:
|
||||
"""Stop the application server."""
|
||||
typer.echo("Stopping server...")
|
||||
typer.secho("✓ Server stopped", fg=typer.colors.GREEN)
|
||||
|
||||
|
||||
@server_app.command("status")
|
||||
def server_status(ctx: typer.Context) -> None:
|
||||
"""Check server status."""
|
||||
typer.echo("Server status: Running")
|
||||
|
||||
|
||||
# User commands
|
||||
@user_app.command("create")
|
||||
def user_create(
|
||||
ctx: typer.Context,
|
||||
username: str = typer.Argument(..., help="Username"),
|
||||
email: str = typer.Argument(..., help="Email address"),
|
||||
admin: bool = typer.Option(False, "--admin", help="Create as admin"),
|
||||
) -> None:
|
||||
"""Create a new user."""
|
||||
verbose = ctx.obj.get("verbose", False)
|
||||
|
||||
if verbose:
|
||||
typer.echo(f"Creating user: {username} ({email})")
|
||||
|
||||
if admin:
|
||||
typer.echo("Creating with admin privileges")
|
||||
|
||||
typer.secho(f"✓ User {username} created", fg=typer.colors.GREEN)
|
||||
|
||||
|
||||
@user_app.command("delete")
|
||||
def user_delete(
|
||||
ctx: typer.Context,
|
||||
username: str = typer.Argument(..., help="Username"),
|
||||
force: bool = typer.Option(False, "--force", help="Force deletion"),
|
||||
) -> None:
|
||||
"""Delete a user."""
|
||||
if not force:
|
||||
confirm = typer.confirm(f"Delete user {username}?")
|
||||
if not confirm:
|
||||
typer.echo("Cancelled")
|
||||
raise typer.Abort()
|
||||
|
||||
typer.secho(f"✓ User {username} deleted", fg=typer.colors.RED)
|
||||
|
||||
|
||||
@user_app.command("list")
|
||||
def user_list(ctx: typer.Context) -> None:
|
||||
"""List all users."""
|
||||
typer.echo("Listing users...")
|
||||
# List logic here
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app()
|
||||
143
skills/typer-patterns/templates/typer-instance.py
Normal file
143
skills/typer-patterns/templates/typer-instance.py
Normal file
@@ -0,0 +1,143 @@
|
||||
"""Typer instance factory pattern template.
|
||||
|
||||
This template demonstrates:
|
||||
- Factory function for creating Typer apps
|
||||
- Better testability
|
||||
- Configuration injection
|
||||
- Dependency management
|
||||
"""
|
||||
|
||||
import typer
|
||||
from typing import Optional, Protocol
|
||||
from pathlib import Path
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
# Configuration
|
||||
@dataclass
|
||||
class Config:
|
||||
"""Application configuration."""
|
||||
|
||||
verbose: bool = False
|
||||
debug: bool = False
|
||||
config_file: Optional[Path] = None
|
||||
|
||||
|
||||
# Service protocol (dependency injection)
|
||||
class StorageService(Protocol):
|
||||
"""Storage service interface."""
|
||||
|
||||
def save(self, data: str) -> None:
|
||||
"""Save data."""
|
||||
...
|
||||
|
||||
def load(self) -> str:
|
||||
"""Load data."""
|
||||
...
|
||||
|
||||
|
||||
class FileStorage:
|
||||
"""File-based storage implementation."""
|
||||
|
||||
def __init__(self, base_path: Path) -> None:
|
||||
self.base_path = base_path
|
||||
|
||||
def save(self, data: str) -> None:
|
||||
"""Save data to file."""
|
||||
self.base_path.write_text(data)
|
||||
|
||||
def load(self) -> str:
|
||||
"""Load data from file."""
|
||||
return self.base_path.read_text()
|
||||
|
||||
|
||||
def create_app(
|
||||
config: Optional[Config] = None, storage: Optional[StorageService] = None
|
||||
) -> typer.Typer:
|
||||
"""Factory function for creating Typer application.
|
||||
|
||||
This pattern allows for:
|
||||
- Easy testing with mocked dependencies
|
||||
- Configuration injection
|
||||
- Multiple app instances with different configs
|
||||
|
||||
Args:
|
||||
config: Application configuration
|
||||
storage: Storage service implementation
|
||||
|
||||
Returns:
|
||||
Configured Typer application
|
||||
"""
|
||||
config = config or Config()
|
||||
storage = storage or FileStorage(Path("data.txt"))
|
||||
|
||||
app = typer.Typer(
|
||||
name="myapp",
|
||||
help="Example CLI with factory pattern",
|
||||
add_completion=True,
|
||||
no_args_is_help=True,
|
||||
rich_markup_mode="rich",
|
||||
)
|
||||
|
||||
@app.command()
|
||||
def save(
|
||||
data: str = typer.Argument(..., help="Data to save"),
|
||||
force: bool = typer.Option(False, "--force", help="Overwrite existing"),
|
||||
) -> None:
|
||||
"""Save data using injected storage."""
|
||||
if config.verbose:
|
||||
typer.echo(f"Saving: {data}")
|
||||
|
||||
try:
|
||||
storage.save(data)
|
||||
typer.secho("✓ Data saved successfully", fg=typer.colors.GREEN)
|
||||
except Exception as e:
|
||||
if config.debug:
|
||||
raise
|
||||
typer.secho(f"✗ Error: {e}", fg=typer.colors.RED, err=True)
|
||||
raise typer.Exit(1)
|
||||
|
||||
@app.command()
|
||||
def load() -> None:
|
||||
"""Load data using injected storage."""
|
||||
try:
|
||||
data = storage.load()
|
||||
typer.echo(data)
|
||||
except FileNotFoundError:
|
||||
typer.secho("✗ No data found", fg=typer.colors.RED, err=True)
|
||||
raise typer.Exit(1)
|
||||
except Exception as e:
|
||||
if config.debug:
|
||||
raise
|
||||
typer.secho(f"✗ Error: {e}", fg=typer.colors.RED, err=True)
|
||||
raise typer.Exit(1)
|
||||
|
||||
@app.command()
|
||||
def status() -> None:
|
||||
"""Show application status."""
|
||||
typer.echo("Application Status:")
|
||||
typer.echo(f" Verbose: {config.verbose}")
|
||||
typer.echo(f" Debug: {config.debug}")
|
||||
typer.echo(f" Config: {config.config_file or 'default'}")
|
||||
|
||||
return app
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Main entry point with configuration setup."""
|
||||
# Parse global options
|
||||
import sys
|
||||
|
||||
verbose = "--verbose" in sys.argv or "-v" in sys.argv
|
||||
debug = "--debug" in sys.argv
|
||||
|
||||
# Create configuration
|
||||
config = Config(verbose=verbose, debug=debug)
|
||||
|
||||
# Create and run app
|
||||
app = create_app(config=config)
|
||||
app()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user