Skip to content

Text, tables and output

BaseCommand inherits Console, which implements IConsole. Inside a command, use self.writeLine(), self.table() and the other inherited methods directly: you do not need to import or instantiate Console. Use this as the main pattern:

from orionis.console.base import BaseCommand
class ImportSummaryCommand(BaseCommand):
signature = 'app:import-summary'
description = 'Demonstrate inherited command output.'
async def handle(self) -> int:
self.writeLine('Import summary')
self.table(['Status', 'Rows'], [['created', 12], ['skipped', 3]])
self.success('Finished', timestamp=False)
return 0

Inherited methods are synchronous except sleep(). Calling them inside async handle() does not make output or prompts asynchronous. Text methods write to stdout; error() does not automatically use stderr or change the exit code.

Status messages with timestamp=True obtain the time through the DateTime facade, available in a booted application. Use timestamp=False in standalone utilities. Styled methods emit ANSI codes; they do not interpret Rich markup such as [bold].

write(*values: object, sep: str | None = " ", end: str | None = "\n",
file: SupportsWrite[str] | None = None, flush: bool = False) -> None
writeLine(message: str) -> None

write() delegates to print(): it converts values to text, places sep between them and appends end. file=None selects stdout; flush=True flushes the buffer. sep=None and end=None use the defaults of print. SupportsWrite[str] describes an object with write(str), not a filename. writeLine() prints one message followed by a newline. Both return None; conversion errors, invalid print() arguments and stream errors propagate.

from io import StringIO
from orionis.console.base import BaseCommand
class WriteValuesCommand(BaseCommand):
signature = 'app:write-values'
description = 'Demonstrate inherited command output.'
async def handle(self) -> int:
self.write('Processed', 24, 'rows', sep=' | ', flush=True)
self.writeLine('Import complete')
buffer = StringIO()
self.write('created', 24, sep=',', end='', file=buffer)
assert buffer.getvalue() == 'created,24'
return 0

SignatureAppearance and use
success(message: str, *, timestamp: bool = True) -> NoneSUCCESS, green background: operation completed successfully.
info(message: str, *, timestamp: bool = True) -> NoneINFO, blue background: process information.
warning(message: str, *, timestamp: bool = True) -> NoneWARNING, yellow background: a condition requiring attention.
fail(message: str, *, timestamp: bool = True) -> NoneFAIL, ANSI 256 background index 166 (orange): a failed operation.
error(message: str, *, timestamp: bool = True) -> NoneERROR, red background: an error diagnostic.

message is the text to print. Keyword-only timestamp controls the date and time; no global Console option replaces this argument. All return None, do not raise because of the displayed status and do not stop the command. Date service and stdout errors propagate.

from orionis.console.base import BaseCommand
class StatusMessagesCommand(BaseCommand):
signature = 'app:status-messages'
description = 'Demonstrate inherited command output.'
async def handle(self) -> int:
self.info('Reading input', timestamp=False)
self.warning('Two rows were skipped', timestamp=False)
self.fail('Import could not be completed', timestamp=False)
self.error('The supplied file has an invalid format', timestamp=False)
self.success('Summary saved', timestamp=False)
return 0

Each method accepts only message: str, prints a line and returns None. They add no label or timestamp. There is no additional validation; stream errors propagate.

SignatureStyle
textSuccess(message: str) -> NoneGreen.
textSuccessBold(message: str) -> NoneBold green.
textInfo(message: str) -> NoneBlue.
textInfoBold(message: str) -> NoneBold blue.
textWarning(message: str) -> NoneYellow.
textWarningBold(message: str) -> NoneBold yellow.
textError(message: str) -> NoneRed.
textErrorBold(message: str) -> NoneBold red.
textMuted(message: str) -> NoneGray.
textMutedBold(message: str) -> NoneBold gray.
textUnderline(message: str) -> NoneUnderlined.
from orionis.console.base import BaseCommand
class StyledTextCommand(BaseCommand):
signature = 'app:styled-text'
description = 'Demonstrate inherited command output.'
async def handle(self) -> int:
self.textSuccess('Created')
self.textSuccessBold('All records created')
self.textInfo('Reading configuration')
self.textInfoBold('Selected connection: sqlite')
self.textWarning('Skipped an existing record')
self.textWarningBold('Review the skipped records')
self.textError('Invalid row')
self.textErrorBold('Import failed')
self.textMuted('Elapsed: 0.2 seconds')
self.textMutedBold('Import details')
self.textUnderline('Summary')
return 0

line() -> None
newLine(count: int = 1) -> None
clear() -> None
clearLine() -> None

line() writes a newline; it does not draw a horizontal rule. newLine() writes count newlines and raises ValueError if count <= 0. Use a positive integer; other types can cause TypeError during comparison or multiplication.

clear() clears the screen and moves the cursor home using ANSI only when stdout is a terminal (isatty()); it writes nothing to redirected output. clearLine() writes \r \r and flushes: it returns to the start and overwrites one character, without guaranteeing that an entire long line is erased. All return None; stream errors propagate.

from orionis.console.base import BaseCommand
class TerminalOutputCommand(BaseCommand):
signature = 'app:terminal-output'
description = 'Demonstrate inherited command output.'
async def handle(self) -> int:
self.clear()
self.write('Preparing...', end='', flush=True)
self.clearLine()
self.writeLine('Ready')
self.line()
self.newLine(2)
return 0

table(headers: list, rows: list) -> None

headers contains column names and rows contains rows. It converts cells to text, calculates the maximum width of each column and draws borders with bold headers. Returns None. Empty headers or rows raise ValueError.

Provide text headers and the same number of cells in every row: width calculation uses zip(), so short rows truncate the columns and extra cells are not displayed. There is no automatic wrapping to terminal width, alignment option or pagination.

from orionis.console.base import BaseCommand
class TableOutputCommand(BaseCommand):
signature = 'app:table-output'
description = 'Demonstrate inherited command output.'
async def handle(self) -> int:
rows = [['customers', 120], ['orders', 48]]
if rows:
self.table(['Resource', 'Imported'], rows)
else:
self.writeLine('No rows to display')
return 0

exception(exception: Exception) -> None

Accepts an Exception instance and displays its type, message and a Rich traceback with max_frames=1, one context line and no local variables. Returns None; an argument not derived from Exception raises TypeError. Displaying it does not rethrow it, record an event or mark the command as failed.

from orionis.console.base import BaseCommand
class CheckInputCommand(BaseCommand):
signature = "app:check-input"
description = "Validate an import quantity."
async def handle(self) -> int:
try:
int("invalid")
except ValueError as error:
self.exception(error)
return 1
return 0

exitSuccess(message: str | None = None) -> None
exitError(message: str | None = None) -> None

When message contains text, the method first calls success() or error() with timestamps enabled. It then requests exit with code 0 or 1. The implementation catches SystemExit and calls os._exit(), terminating the process immediately. These methods do not return a result to Reactor; finally blocks and application lifecycle cleanup cannot be relied on.

For ordinary commands, return an integer from handle(), as in the previous example. Use these methods only when the entire process running the command, including its other tasks, must terminate:

from orionis.console.base import BaseCommand
class ProcessExitCommand(BaseCommand):
signature = 'app:process-exit'
description = 'Demonstrate inherited command output.'
def terminate_process(self, success: bool) -> None:
if success:
self.exitSuccess()
else:
self.exitError()

Defining the class does not run the terminate_process() helper; it terminates the process only when called. Optional messages require the same booted application as other timestamped output. Do not call these methods from a programmatically invoked command when the calling process must continue.

Executor implements IExecutor. It does not execute processes and is not inherited by BaseCommand: it displays execution status. Its constructor accepts no arguments, and all three methods return None:

running(program: str, time: str = "") -> None
done(program: str, time: str = "") -> None
fail(program: str, time: str = "") -> None

program is a label; time is text already calculated by the caller, not a timer. It prints a timestamp, dot padding and RUNNING, DONE or FAIL, flushing stdout. done() and fail() prepend ~ to the time text. It uses DateTime, so this example belongs in a booted application command. Date and stream errors propagate; displaying FAIL does not raise an exception.

from time import perf_counter
from orionis.console.base import BaseCommand
from orionis.console.output.executor import Executor
class MeasureCommand(BaseCommand):
signature = "app:measure"
description = "Show execution status."
async def handle(self) -> int:
output = Executor()
output.running("Inventory summary")
started = perf_counter()
try:
total = sum(range(100))
except Exception as error:
output.fail("Inventory summary", f"{perf_counter() - started:.3f}s")
self.exception(error)
return 1
output.done("Inventory summary", f"{perf_counter() - started:.3f}s")
self.writeLine(f"Total: {total}")
return 0

HelpCommand implements IHelpCommand. It is a static parser helper, not a command superclass:

HelpCommand.parseActions(actions: list[argparse.Action]) -> dict[str, Any]
HelpCommand.printActions(command_name: str, actions: list[argparse.Action],
*, is_error: bool = False) -> None

parseActions() classifies argparse actions. Returns help (dict or None), positionals (list), optionals (list) and subcommands (dict). Each argument contains action_class, dest, flags, nargs, const, default, type, choices, required, help and metavar. Subcommands include their description and recursively classified arguments. It does not execute commands or automatically convert these values to JSON: choices and default can contain Python objects.

printActions() displays a panel and tables for arguments, options and subcommands. is_error=True adds an invalid-usage diagnostic; it still returns None, without raising a parsing error or changing the exit code itself. It accepts previously constructed actions; invalid-object and rendering errors propagate.

import argparse
from orionis.console.output.help_command import HelpCommand
parser = argparse.ArgumentParser(prog="app:report")
parser.add_argument("name", help="Report name")
parser.add_argument("--format", choices=["csv", "json"], default="csv")
metadata = HelpCommand.parseActions(parser._actions)
assert metadata["positionals"][0]["dest"] == "name"
HelpCommand.printActions("app:report", parser._actions)
HelpCommand.printActions("app:report", parser._actions, is_error=True)

The example accesses parser._actions because this API specifically accepts argparse actions; let Reactor construct and display help for application commands.

ANSIColors is an Enum; each member exposes its sequence as .value. The Console methods above already select these codes. The following 24 members are available for custom presentation; exact color reproduction depends on the terminal:

MemberSequence (ESC means \x1b)Purpose
DEFAULTESC[0mReset styles and colors.
BG_INFOESC[44mBlue background.
BG_ERRORESC[41mRed background.
BG_FAILESC[48;5;166m256-color background, index 166.
BG_WARNINGESC[43mYellow background.
BG_SUCCESSESC[42mGreen background.
TEXT_INFOESC[34mBlue text.
TEXT_ERRORESC[91mBright red text.
TEXT_WARNINGESC[33mYellow text.
TEXT_SUCCESSESC[32mGreen text.
TEXT_WHITEESC[97mBright white text.
TEXT_MUTEDESC[90mGray text.
TEXT_BOLD_INFOESC[1;34mBold blue text.
TEXT_BOLD_ERRORESC[1;91mBold bright red text.
TEXT_BOLD_WARNINGESC[1;33mBold yellow text.
TEXT_BOLD_SUCCESSESC[1;32mBold green text.
TEXT_BOLD_WHITEESC[1;97mBold bright white text.
TEXT_BOLD_MUTEDESC[1;90mBold gray text.
TEXT_BOLDESC[1mBold.
TEXT_STYLE_UNDERLINEESC[4mUnderline.
CYANESC[36mCyan text.
DIMESC[2mDim intensity.
MAGENTAESC[35mMagenta text.
ITALICESC[3mItalic.
from orionis.console.enums.styles import ANSIColors
from orionis.console.base import BaseCommand
class AnsiOutputCommand(BaseCommand):
signature = 'app:ansi-output'
description = 'Demonstrate inherited command output.'
async def handle(self) -> int:
self.writeLine(f'{ANSIColors.CYAN.value}Report details{ANSIColors.DEFAULT.value}')
return 0

Continue with interactive questions, progress or programmatic execution.

In a utility that does not extend BaseCommand, you can import and construct Console. This is an independent use; inside a command, use inherited methods through self. Console() takes no arguments.

from orionis.console import Console
console = Console()
console.writeLine("Import summary")
console.table(["Status", "Rows"], [["created", 12], ["skipped", 3]])
console.success("Finished", timestamp=False)