Skip to content

xnano.cli.command

xnano.cli.command

xnano.cli.command


Define typed commands and subcommands from ordinary Python methods.

Classes:

  • Command

    A command or subcommand group.

  • HelpException

    Stop command parsing after help has been requested.

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.