Skip to content

xnano.cli

xnano.cli

xnano.cli


Build typed commands, options, subcommands, and help screens.

Modules:

Classes:

  • Command

    A command or subcommand group.

  • HelpException

    Stop command parsing after help has been requested.

  • CliError

    User-facing CLI failure.

  • Argument

    Positional argument metadata.

  • Option

    Option metadata attached via default or Annotated.

Functions:

  • render_help

    Render help for command, optionally via terminal components.

Command dataclass

Command(
    name: str | None = None,
    description: str | None = None,
    strict: bool = False,
    show_help: bool = True,
    help: bool = True,
)

A command or subcommand group.

Attributes:

Example

command = Command(name="hello") @command ... def greet(name: str) -> str: ... return f"Hello, {name}" command.run(["Ada"]) 'Hello, Ada'

Methods:

  • option

    Attach option names and help text to a command parameter.

  • command

    Decorator to register a subcommand.

  • register_callback

    Register the main callback for this command.

  • add_subcommand

    Add a subcommand programmatically.

  • parse_arguments

    Parse arguments without exiting the process.

  • run

    Parse arguments and run the command, exiting on errors/help.

  • get_help

    Return plain help text for this command.

  • __call__

    Register a callback or run with sys.argv.

name class-attribute instance-attribute

name: str | None = None

Command name shown in usage.

description class-attribute instance-attribute

description: str | None = None

Command summary shown in help.

strict class-attribute instance-attribute

strict: bool = False

Whether annotations are validated strictly.

show_help class-attribute instance-attribute

show_help: bool = True

Whether -h and --help are enabled.

help class-attribute instance-attribute

help: bool = True

Compatibility alias for show_help.

UNSET class-attribute instance-attribute

UNSET: Any = dataclasses.field(
    default=UNSET, init=False, repr=False
)

Sentinel used for parameters without defaults.

parameters property

parameters: list[_Parameter]

Resolved parameters for help rendering.

subcommands property

subcommands: dict[str, 'Command']

Registered subcommands.

option staticmethod

option(
    name_or_flags: str | list[str],
    *,
    default: Any = None,
    help: str | None = None,
    is_flag: bool | None = None
) -> Callable[[Callable[..., Any]], Callable[..., Any]]

Attach option names and help text to a command parameter.

Source code in xnano/cli/command.py
@staticmethod
def option(
    name_or_flags: str | list[str],
    *,
    default: Any = None,
    help: str | None = None,
    is_flag: bool | None = None,
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
    """Attach option names and help text to a command parameter."""

    def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
        options = getattr(func, "_cli_options", None)
        if options is None:
            options = []
            setattr(func, "_cli_options", options)
        if isinstance(name_or_flags, str):
            flags = [name_or_flags]
        else:
            flags = list(name_or_flags)
        options.append(
            {
                "flags": flags,
                "default": default,
                "help": help,
                "is_flag": is_flag,
            }
        )
        return func

    return decorator

command

command(
    name: str | None = None,
    *,
    description: str | None = None
) -> Callable[[Callable[..., Any]], Callable[..., Any]]

Decorator to register a subcommand.

Source code in xnano/cli/command.py
def command(
    self,
    name: str | None = None,
    *,
    description: str | None = None,
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
    """Decorator to register a subcommand."""

    def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
        func_name = getattr(func, "__name__", None) or "command"
        command_name = name or func_name.replace("_", "-")
        command_description = description or func.__doc__
        subcommand = Command(
            name=command_name,
            description=command_description,
            strict=self.strict,
            help=self.help,
        )
        subcommand._callback = func
        subcommand._register_from_function(func)
        self._subcommands[command_name] = subcommand
        self._parser_dirty = True
        return func

    return decorator

register_callback

register_callback(
    func: Callable[..., Any],
) -> Callable[..., Any]

Register the main callback for this command.

Source code in xnano/cli/command.py
def register_callback(
    self, func: Callable[..., Any]
) -> Callable[..., Any]:
    """Register the main callback for this command."""
    self._callback = func
    self._register_from_function(func)
    self._parser_dirty = True
    return func

add_subcommand

add_subcommand(subcommand: 'Command') -> None

Add a subcommand programmatically.

Source code in xnano/cli/command.py
def add_subcommand(self, subcommand: "Command") -> None:
    """Add a subcommand programmatically."""
    if not subcommand.name:
        raise CliError("Subcommand must have a name to be added.")
    self._subcommands[subcommand.name] = subcommand
    self._parser_dirty = True

parse_arguments

parse_arguments(
    arguments: list[str],
) -> tuple["Command", dict[str, Any]]

Parse arguments without exiting the process.

Raises:

Source code in xnano/cli/command.py
def parse_arguments(
    self, arguments: list[str]
) -> tuple["Command", dict[str, Any]]:
    """Parse arguments without exiting the process.

    Raises:
        HelpRequested: When help was requested.
        CliError: On usage / validation failures.
    """
    # Prefer the stable hand-parser semantics for drop-in parity with
    # existing tests, while exposing argparse caching for future work.
    return self._parse_manual(arguments)

run

run(arguments: list[str] | None = None) -> Any

Parse arguments and run the command, exiting on errors/help.

Source code in xnano/cli/command.py
def run(self, arguments: list[str] | None = None) -> Any:
    """Parse arguments and run the command, exiting on errors/help."""
    if arguments is None:
        arguments = sys.argv[1:]
    try:
        target, parsed = self.parse_arguments(arguments)
    except HelpRequested as help_requested:
        print(render_help(help_requested.command), end="")
        sys.exit(0)
    except HelpException as help_exception:
        print(render_help(help_exception.command), end="")
        sys.exit(0)
    except CliError as error:
        print_error(error.message, error.command or self)
        sys.exit(error.exit_code)
    except ValueError as error:
        # Compatibility with callers raising plain ValueError.
        print_error(str(error), self)
        sys.exit(2)

    if target._callback is None:
        print(render_help(target), end="")
        sys.exit(0)
    return target._callback(**parsed)

get_help

get_help() -> str

Return plain help text for this command.

Source code in xnano/cli/command.py
def get_help(self) -> str:
    """Return plain help text for this command."""
    return format_plain_help(self)

__call__

__call__(*args: Any, **kwargs: Any) -> Any

Register a callback or run with sys.argv.

Source code in xnano/cli/command.py
def __call__(self, *args: Any, **kwargs: Any) -> Any:
    """Register a callback or run with ``sys.argv``."""
    if len(args) == 1 and callable(args[0]) and not kwargs:
        return self.register_callback(args[0])
    return self.run(sys.argv[1:])

HelpException dataclass

HelpException(command: Command)

Bases: Exception

Stop command parsing after help has been requested.

Attributes:

command instance-attribute

command: Command

Command whose help should be displayed.

CliError dataclass

CliError(
    message: str,
    command: "Command | None" = None,
    exit_code: int = 2,
)

Bases: Exception

User-facing CLI failure.

Attributes:

  • message (str) –

    Error text shown to the user.

  • command ('Command | None') –

    Command that failed, when known.

  • exit_code (int) –

    Process exit status used by run().

message instance-attribute

message: str

Error text shown to the user.

command class-attribute instance-attribute

command: 'Command | None' = None

Command that failed.

exit_code class-attribute instance-attribute

exit_code: int = 2

Process exit status.

Argument

Argument(
    *,
    help: str | None = None,
    metavar: str | None = None,
    choices: Sequence[Any] | None = None
)

Positional argument metadata.

Attributes:

  • help

    Help text for this argument.

  • metavar

    Optional metavar override.

  • choices

    Optional allowed values.

Example

argument = Argument(help="Input file", metavar="PATH") argument.metavar 'PATH'

Source code in xnano/cli/parameters.py
def __init__(
    self,
    *,
    help: str | None = None,
    metavar: str | None = None,
    choices: Sequence[Any] | None = None,
) -> None:
    self.help = help
    self.metavar = metavar
    self.choices = choices

Option

Option(
    *flags: str,
    help: str | None = None,
    metavar: str | None = None,
    choices: Sequence[Any] | None = None,
    hidden: bool = False
)

Option metadata attached via default or Annotated.

Attributes:

  • flags

    Short/long option flags (e.g. "-f", "--force").

  • help

    Help text for this option.

  • metavar

    Optional metavar override.

  • choices

    Optional allowed values.

  • hidden

    When True, omit from help.

Example

option = Option("-f", "--force", help="Overwrite output") option.flags ('-f', '--force')

Source code in xnano/cli/parameters.py
def __init__(
    self,
    *flags: str,
    help: str | None = None,
    metavar: str | None = None,
    choices: Sequence[Any] | None = None,
    hidden: bool = False,
) -> None:
    self.flags = tuple(flags)
    self.help = help
    self.metavar = metavar
    self.choices = choices
    self.hidden = hidden

render_help

render_help(
    command: "Command", *, stream: TextIO | None = None
) -> str

Render help for command, optionally via terminal components.

Parameters:

  • command ('Command') –

    Command to document.

  • stream (TextIO | None, default: None ) –

    Unused except for TTY detection.

Returns:

  • str

    Help text. Styled rendering falls back to plain text when the

  • str

    terminal path is unavailable.

Source code in xnano/cli/help.py
def render_help(
    command: "Command",
    *,
    stream: TextIO | None = None,
) -> str:
    """Render help for ``command``, optionally via terminal components.

    Args:
        command: Command to document.
        stream: Unused except for TTY detection.

    Returns:
        Help text. Styled rendering falls back to plain text when the
        terminal path is unavailable.
    """
    plain = format_plain_help(command)
    if not should_use_styled_help(stream):
        return plain
    try:
        from xnano.components.text import Text
        from xnano.terminal import Terminal

        # One-shot styled render; still return plain for callers that
        # capture strings (tests, pipes). Styled paint is best-effort.
        Terminal().render(Text(plain))
    except Exception:
        pass
    return plain