Command Types
The Ascender CLI engine provides two distinct command types to handle different use cases. Understanding when to use each type is crucial for building an effective command-line interface.
BasicCLI Commands
BasicCLI commands are designed for single-action operations. They implement one execute() method that handles all the command's functionality.
When to Use BasicCLI
- Simple Operations: Version display, status checks, configuration dumps
- Build Tasks: Compilation, bundling, deployment
- Initialization: Project setup, scaffolding
- Standalone Actions: Any command that performs one specific task
Structure
from ascender.core.cli_engine import Command, BasicCLI, Parameter
@Command(name="command_name", description="Command description")
class MyCommand(BasicCLI):
# Define arguments as class attributes
arg_name: type = Parameter(
default_value,
description="Argument description",
names=["--arg-name", "-a"]
)
def execute(self) -> None:
# Access arguments via self.arg_name
pass
Command Pattern
Example
@Command(name="version", description="Display framework version")
class VersionCommand(BasicCLI):
verbose: bool = Parameter(
False,
description="Show detailed version information",
names=["--verbose", "-v"]
)
def execute(self) -> None:
if self.verbose:
print("Ascender Framework v1.0.0")
print("Build: 2024.10.18")
print("Python: 3.11+")
else:
print("Ascender Framework v1.0.0")
Usage:
GenericCLI Commands
GenericCLI commands are designed for multi-command groups. They contain multiple methods, each representing a different subcommand within the same functional area.
When to Use GenericCLI
- Related Operations: Code generation, database management, testing
- CRUD Operations: Create, read, update, delete workflows
- Multi-Step Processes: Commands that have multiple related actions
- Grouped Functionality: When you have several commands that logically belong together
Structure
from ascender.core.cli_engine import Command, Handler, GenericCLI
from typing import Any
@Command(name="group_name", description="Group description")
class MyCommandGroup(GenericCLI):
@Handler("subcommand1", description="First subcommand")
def subcommand1(self, **kwargs: Any) -> None:
# First subcommand implementation
pass
@Handler("subcommand2", description="Second subcommand", is_coroutine=True)
async def subcommand2(self, **kwargs: Any) -> None:
# Async subcommand implementation
pass
The @Handler Decorator
GenericCLI subcommands must be decorated with the @Handler decorator to register them properly with the CLI engine. The Handler decorator:
- Registers subcommands: Makes methods available as CLI subcommands
- Supports coroutines: Can handle both sync and async methods with
is_coroutine=True - Parameter parsing: Automatically parses method signatures for CLI arguments
- Metadata extraction: Stores command metadata for help generation
@Handler(
"command_name", # Subcommand name
description="Command description", # Help text
is_coroutine=False, # Set to True for async methods
**kwargs # Additional options
)
Command Pattern
Example
@Command(name="generate", description="Generate project components")
class GenerateCommand(GenericCLI):
@Handler("controller", description="Generate a new controller")
def controller(self, name: str, path: str = "src/controllers", **kwargs: Any) -> None:
"""Generate a new controller."""
print(f"Creating controller {name} in {path}")
# Controller generation logic here
@Handler("service", description="Generate a new service")
def service(self, name: str, interface: bool = False, **kwargs: Any) -> None:
"""Generate a new service."""
print(f"Creating service {name}")
if interface:
print("Including interface definition")
# Service generation logic here
@Handler("migrate", description="Run database migrations", is_coroutine=True)
async def migrate(self, rollback: bool = False, **kwargs: Any) -> None:
"""Async database migration handler."""
if rollback:
print("Rolling back migrations...")
else:
print("Running migrations...")
# Async migration logic here
```**Usage**:
```bash
ascender generate controller UserController
ascender generate controller UserController --path src/api/controllers
ascender generate service UserService --interface
ascender generate model User --database main
Key Differences
| Aspect | BasicCLI | GenericCLI |
|---|---|---|
| Purpose | Single action | Group of related actions |
| Methods | One execute() method |
Multiple command methods |
| CLI Pattern | ascender <cmd> [args] |
ascender <group> <subcmd> [args] |
| Use Cases | Version, build, init | Generate, database, test |
| Complexity | Simple, focused | Organized, multi-functional |
| Arguments | Direct to execute | Per-method arguments |
Choosing the Right Type
Use BasicCLI when:
- ✅ Your command does one specific thing
- ✅ You don't anticipate adding related subcommands
- ✅ The functionality is simple and focused
- ✅ You want a direct command-to-action mapping
Use GenericCLI when:
- ✅ You have multiple related commands
- ✅ Commands share a common theme or domain
- ✅ You want to organize commands into logical groups
- ✅ You anticipate adding more related commands in the future
Method Discovery (GenericCLI)
For GenericCLI commands, the CLI engine discovers subcommands through the @Handler decorator:
- Handler Registration: Only methods decorated with
@Handlerbecome subcommands - Metadata Parsing: Handler extracts parameter information from method signatures
- Coroutine Support: Async methods are supported when
is_coroutine=Trueis specified - Help Generation: Handler descriptions and method docstrings generate help text
- Parameter Validation: Automatic type checking and argument validation
Handler Decorator Required
Unlike basic method discovery, GenericCLI subcommands must use the @Handler decorator. This ensures proper registration and metadata extraction.
Async Support
The CLI engine fully supports async subcommands. Simply set is_coroutine=True in the Handler decorator and define your method as async def.
Method Visibility
Only methods with the @Handler decorator are registered as subcommands. Public methods without this decorator will be ignored by the CLI engine.