Cleo allows you to create beautiful and testable command-line interfaces.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
The command development system provides the foundation for creating individual CLI commands with arguments, options, and business logic. Commands inherit from base classes and define their interface through class attributes and method implementations.
Abstract base class that defines the fundamental command interface and lifecycle.
class BaseCommand:
name: str | None = None # Command name (required)
description: str = "" # Short description for help
help: str = "" # Detailed help text
enabled: bool = True # Whether command is enabled
hidden: bool = False # Whether to hide from command lists
def configure(self) -> None:
"""Configure the command definition. Called during initialization."""
def execute(self, io: IO) -> int:
"""
Execute the command with provided IO.
Args:
io (IO): Input/output interface
Returns:
int: Exit code (0 for success)
"""
def run(self, io: IO) -> int:
"""
Run the command through the full lifecycle.
Args:
io (IO): Input/output interface
Returns:
int: Exit code from execution
"""Complete command implementation with UI helpers and convenience methods.
class Command(BaseCommand):
arguments: ClassVar[list[Argument]] = [] # Command arguments definition
options: ClassVar[list[Option]] = [] # Command options definition
aliases: ClassVar[list[str]] = [] # Command aliases
usages: ClassVar[list[str]] = [] # Usage examples
commands: ClassVar[list[BaseCommand]] = [] # Sub-commands
def __init__(self) -> None: ...
@property
def io(self) -> IO:
"""Get the IO interface for this command."""
def handle(self) -> int:
"""
Command business logic implementation (abstract).
Returns:
int: Exit code (0 for success)
"""
# Command execution methods
def call(self, name: str, args: str | None = None) -> int: ...
def call_silent(self, name: str, args: str | None = None) -> int: ...
# User interaction methods
def confirm(self, question: str, default: bool = False, true_answer_regex: str = r"(?i)^y") -> bool: ...
def ask(self, question: str | Question, default: Any | None = None) -> Any: ...
def secret(self, question: str | Question, default: Any | None = None) -> Any: ...
def choice(self, question: str, choices: list[str], default: Any | None = None,
attempts: int | None = None, multiple: bool = False) -> Any: ...
# Output methods
def write(self, text: str, style: str | None = None) -> None: ...
def line_error(self, text: str, style: str | None = None, verbosity: Verbosity = Verbosity.NORMAL) -> None: ...
def info(self, text: str) -> None: ...
def comment(self, text: str) -> None: ...
def question(self, text: str) -> None: ...
def overwrite(self, text: str) -> None: ...
# Table rendering
def table(self, header: str | None = None, rows: Rows | None = None, style: str | None = None) -> Table: ...
def table_separator(self) -> TableSeparator: ...
def render_table(self, headers: str, rows: Rows, style: str | None = None) -> None: ...
# Progress indicators
def progress_bar(self, max: int = 0) -> ProgressBar: ...
def progress_indicator(self, fmt: str | None = None, interval: int = 100, values: list[str] | None = None) -> ProgressIndicator: ...
def spin(self, start_message: str, end_message: str, fmt: str | None = None,
interval: int = 100, values: list[str] | None = None) -> ContextManager[ProgressIndicator]: ...
# Styling
def add_style(self, name: str, fg: str | None = None, bg: str | None = None, options: list[str] | None = None) -> None: ...
# Utility
def create_question(self, question: str, type: Literal["choice", "confirmation"] | None = None, **kwargs: Any) -> Question: ...Access command arguments and options provided by the user.
def argument(self, name: str) -> Any:
"""
Get an argument value by name.
Args:
name (str): Argument name
Returns:
Any: Argument value or None if not provided
"""
def option(self, name: str) -> Any:
"""
Get an option value by name.
Args:
name (str): Option name
Returns:
Any: Option value, default value, or None
"""Helper functions to create argument and option definitions for commands.
def argument(name: str, description: str | None = None, optional: bool = False,
multiple: bool = False, default: Any | None = None) -> Argument:
"""
Create an argument definition.
Args:
name (str): Argument name
description (str | None): Help description
optional (bool): Whether argument is optional
multiple (bool): Whether argument accepts multiple values
default (Any | None): Default value for optional arguments
Returns:
Argument: Configured argument definition
"""
def option(long_name: str, short_name: str | None = None, description: str | None = None,
flag: bool = True, value_required: bool = True, multiple: bool = False,
default: Any | None = None) -> Option:
"""
Create an option definition.
Args:
long_name (str): Long option name (--option)
short_name (str | None): Short option name (-o)
description (str | None): Help description
flag (bool): Whether this is a boolean flag
value_required (bool): Whether option requires a value
multiple (bool): Whether option accepts multiple values
default (Any | None): Default value
Returns:
Option: Configured option definition
"""Methods for producing output and interacting with users.
def line(self, text: str, style: str | None = None, verbosity: Verbosity = Verbosity.NORMAL) -> None:
"""
Write a line of text to output.
Args:
text (str): Text to output
style (str | None): Style name for formatting
verbosity (Verbosity | None): Minimum verbosity level
"""
def write(self, text: str, new_line: bool = False, verbosity: Verbosity | None = None) -> None:
"""
Write text to output.
Args:
text (str): Text to output
new_line (bool): Whether to add a newline
verbosity (Verbosity | None): Minimum verbosity level
"""
def confirm(self, question: str, default: bool = False) -> bool:
"""
Ask a yes/no confirmation question.
Args:
question (str): Question text
default (bool): Default answer
Returns:
bool: User's answer
"""
def ask(self, question: str | Question, default: Any | None = None) -> Any:
"""
Ask a question and get user input.
Args:
question (str | Question): Question text or Question object
default (Any | None): Default answer
Returns:
Any: User's answer
"""
def choice(self, question: str, choices: list[str], default: Any | None = None) -> Any:
"""
Ask a multiple choice question.
Args:
question (str): Question text
choices (list[str]): Available choices
default (Any | None): Default choice
Returns:
Any: Selected choice
"""Methods for controlling command execution and calling other commands.
def call(self, command: str, args: str = "") -> int:
"""
Call another command from this command.
Args:
command (str): Command name to call
args (str): Arguments to pass to the command
Returns:
int: Exit code from called command
"""
def call_silent(self, command: str, args: str = "") -> int:
"""
Call another command silently (suppressing output).
Args:
command (str): Command name to call
args (str): Arguments to pass
Returns:
int: Exit code from called command
"""Methods for adding custom styles and formatting output.
def add_style(self, name: str, fg: str | None = None, bg: str | None = None,
options: list[str] | None = None) -> None:
"""
Add a custom style for text formatting.
Args:
name (str): Style name
fg (str | None): Foreground color
bg (str | None): Background color
options (list[str] | None): Formatting options (bold, underscore, etc.)
"""Defines command arguments with validation and type information.
class Argument:
def __init__(self, name: str, required: bool = True, is_list: bool = False,
description: str | None = None, default: Any | None = None): ...
@property
def name(self) -> str: ...
@property
def is_required(self) -> bool: ...
@property
def is_optional(self) -> bool: ...
@property
def is_list(self) -> bool: ...
@property
def description(self) -> str: ...
@property
def default(self) -> Any: ...Defines command options with flags, values, and validation.
class Option:
def __init__(self, name: str, shortcut: str | None = None, flag: bool = True,
requires_value: bool = True, is_list: bool = False,
description: str | None = None, default: Any | None = None): ...
@property
def name(self) -> str: ...
@property
def shortcut(self) -> str: ...
@property
def is_flag(self) -> bool: ...
@property
def requires_value(self) -> bool: ...
@property
def is_list(self) -> bool: ...
@property
def description(self) -> str: ...
@property
def default(self) -> Any: ...from cleo.commands.command import Command
from cleo.helpers import argument, option
class ProcessCommand(Command):
name = "process"
description = "Process data files"
arguments = [
argument("input_file", description="Input file path"),
argument("output_file", description="Output file path", optional=True)
]
options = [
option("format", "f", description="Output format", flag=False, default="json"),
option("verbose", "v", description="Verbose output", flag=True)
]
def handle(self):
input_file = self.argument("input_file")
output_file = self.argument("output_file") or f"{input_file}.processed"
format = self.option("format")
verbose = self.option("verbose")
if verbose:
self.line(f"Processing <info>{input_file}</info>")
# Process the file
result = self.process_file(input_file, format)
# Write output
with open(output_file, 'w') as f:
f.write(result)
self.line(f"<comment>Processed data written to {output_file}</comment>")
return 0
def process_file(self, file_path, format):
# Implementation here
return "processed data"class ConfigCommand(Command):
name = "config"
description = "Configure application settings"
def handle(self):
# Get configuration values interactively
host = self.ask("Database host", "localhost")
port = self.ask("Database port", 5432)
# Confirm settings
if self.confirm(f"Connect to {host}:{port}?", True):
# Multiple choice for environment
env = self.choice(
"Environment",
["development", "staging", "production"],
"development"
)
self.line(f"<info>Configuration saved for {env} environment</info>")
else:
self.line("<error>Configuration cancelled</error>")
return 1
return 0class DatabaseCommand(Command):
name = "db"
description = "Database management commands"
def handle(self):
# Show available sub-commands
self.line("Available database commands:")
self.line(" db:migrate - Run database migrations")
self.line(" db:seed - Seed database with test data")
self.line(" db:reset - Reset database")
return 0
class MigrateCommand(Command):
name = "db:migrate"
description = "Run database migrations"
def handle(self):
self.line("Running migrations...")
# Migration logic here
self.line("<info>Migrations completed</info>")
return 0class DeployCommand(Command):
name = "deploy"
description = "Deploy the application"
def handle(self):
self.line("Starting deployment...")
# Run tests first
if self.call("test") != 0:
self.line("<error>Tests failed, aborting deployment</error>")
return 1
# Build the application
self.call("build", "--production")
# Deploy silently
result = self.call_silent("upload", "--target production")
if result == 0:
self.line("<info>Deployment successful!</info>")
else:
self.line("<error>Deployment failed</error>")
return resultInstall with Tessl CLI
npx tessl i tessl/pypi-cleo