Skip to content

rats.cli


rats.cli

Uses rats.cli to streamline the creation of CLI commands written with Click.

__all__ = ['ClickApp', 'CommandId', 'CompositeContainer', 'Container', 'PluginServices', 'attach', 'command', 'create_group', 'get_class_commands', 'get_class_groups', 'group'] module-attribute

ClickApp(group, commands)

Bases: apps.Executable

...

Not sure this is the right interface.

Source code in rats/cli/_app.py
def __init__(
    self,
    group: click.Group,
    commands: Container,
) -> None:
    """Not sure this is the right interface."""
    self._group = group
    self._commands = commands

execute()

This app executes a click application after letting rats plugins attach commands.

Source code in rats/cli/_app.py
def execute(self) -> None:
    """This app executes a click application after letting rats plugins attach commands."""
    self._commands.attach(self._group)
    self._group.main()

CommandId

Bases: NamedTuple

name instance-attribute

CompositeContainer(*containers)

Bases: Container

Source code in rats/cli/_container.py
def __init__(self, *containers: Container) -> None:
    self._containers = containers

attach(group)

Source code in rats/cli/_container.py
def attach(self, group: click.Group) -> None:
    for container in self._containers:
        container.attach(group)

Container

Bases: Protocol

A container that can attach click commands to a click group.

attach(group)

...

Source code in rats/cli/_container.py
def attach(self, group: click.Group) -> None:
    """..."""

    def cb(_method: Callable[..., None], *args: Any, **kwargs: Any) -> None:
        """
        Callback handed to `click.Command`. Calls the method with matching name on this class.

        When the command is decorated with `@click.params` and `@click.option`, `click` will
        call this callback with the parameters in the order they were defined. This callback
        then calls the method with the same name on this class, passing the parameters in
        reverse order. This is because the method is defined with the parameters in the
        reverse order to the decorator, so we need to reverse them again to get the correct
        order.
        """
        _method(*args, **kwargs)

    commands = get_class_commands(type(self))
    tates = commands.annotations

    for tate in tates:
        method = getattr(self, tate.name)
        params = list(reversed(getattr(method, "__click_params__", [])))
        logger.debug(tate.namespace)
        for command in tate.groups:
            if tate.namespace == "commands":
                group.add_command(
                    click.Command(
                        name=command.name,
                        callback=partial(cb, method),
                        short_help=method.__doc__,
                        params=params,
                    )
                )

PluginServices

EVENTS = _PluginEvents class-attribute instance-attribute

click_command(cmd_id) staticmethod

Source code in rats/cli/_plugin.py
@staticmethod
def click_command(cmd_id: apps.ServiceId[apps.Executable]) -> apps.ServiceId[click.Group]:
    return cast(apps.ServiceId[click.Group], cmd_id)

sub_command(parent, name) staticmethod

Source code in rats/cli/_plugin.py
@staticmethod
def sub_command(
    parent: apps.ServiceId[apps.Executable],
    name: str,
) -> apps.ServiceId[apps.Executable]:
    return apps.ServiceId(f"{parent.name}[{name}]")

attach(group, command, *commands)

Source code in rats/cli/_plugin.py
def attach(
    group: click.Group,
    command: click.Command | click.Group,
    *commands: click.Command | click.Group,
) -> None:
    group.add_command(command)
    for c in commands:
        group.add_command(c)

command(command_id=AUTO_COMMAND)

Source code in rats/cli/_annotations.py
def command(command_id: CommandId = AUTO_COMMAND) -> Callable[..., apps.Executable]:
    def decorator(fn: T) -> T:
        if command_id == AUTO_COMMAND:
            cmd_name = fn.__name__.replace("_", "-").strip("-")
            return anns.annotation("commands", CommandId(cmd_name))(fn)
        return anns.annotation("commands", command_id)(fn)

    return decorator  # type: ignore[reportReturnType]

create_group(group, container)

Source code in rats/cli/_plugin.py
def create_group(group: click.Group, container: Container) -> click.Group:
    container.attach(group)
    return group

get_class_commands(cls)

Source code in rats/cli/_annotations.py
def get_class_commands(cls: type) -> anns.AnnotationsContainer:
    return anns.get_class_annotations(cls).with_namespace("commands")

get_class_groups(cls)

Source code in rats/cli/_annotations.py
def get_class_groups(cls: type) -> anns.AnnotationsContainer:
    return anns.get_class_annotations(cls).with_namespace("command-groups")

group(command_id=AUTO_COMMAND)

Source code in rats/cli/_annotations.py
def group(command_id: CommandId = AUTO_COMMAND) -> Callable[..., apps.Executable]:
    def decorator(fn: T) -> T:
        if command_id == AUTO_COMMAND:
            return anns.annotation("command-groups", CommandId(fn.__name__.replace("_", "-")))(fn)
        return anns.annotation("commands", command_id)(fn)

    return decorator  # type: ignore[reportReturnType]