Skip to content

Index

provide.foundation.cli.click

Click CLI framework adapter.

Provides Click-specific implementation of the CLIAdapter protocol.

Classes

ClickAdapter

Click framework adapter.

Implements the CLIAdapter protocol for the Click framework, converting framework-agnostic CommandInfo objects to Click commands and groups.

Examples:

>>> adapter = ClickAdapter()
>>> command = adapter.build_command(command_info)
>>> isinstance(command, click.Command)
True
Functions
build_command
build_command(info: CommandInfo) -> click_types.Command

Build Click command from CommandInfo.

Parameters:

Name Type Description Default
info CommandInfo

Framework-agnostic command information

required

Returns:

Type Description
Command

Click Command object

Raises:

Type Description
CLIBuildError

If command building fails

Source code in provide/foundation/cli/click/adapter.py
def build_command(self, info: CommandInfo) -> click_types.Command:
    """Build Click command from CommandInfo.

    Args:
        info: Framework-agnostic command information

    Returns:
        Click Command object

    Raises:
        CLIBuildError: If command building fails

    """
    return build_click_command_from_info(info)
build_group
build_group(
    name: str,
    commands: list[CommandInfo] | None = None,
    registry: Registry | None = None,
    **kwargs: Any
) -> click_types.Group

Build Click group with commands.

Parameters:

Name Type Description Default
name str

Group name

required
commands list[CommandInfo] | None

List of CommandInfo objects (or None to use registry)

None
registry Registry | None

Command registry

None
**kwargs Any

Additional Click Group options

{}

Returns:

Type Description
Group

Click Group object

Raises:

Type Description
CLIBuildError

If group building fails

Source code in provide/foundation/cli/click/adapter.py
def build_group(
    self,
    name: str,
    commands: list[CommandInfo] | None = None,
    registry: Registry | None = None,
    **kwargs: Any,
) -> click_types.Group:
    """Build Click group with commands.

    Args:
        name: Group name
        commands: List of CommandInfo objects (or None to use registry)
        registry: Command registry
        **kwargs: Additional Click Group options

    Returns:
        Click Group object

    Raises:
        CLIBuildError: If group building fails

    """
    # If commands is a list of CommandInfo, extract names
    command_names = None
    if commands:
        command_names = [cmd.name for cmd in commands]

    return create_command_group(
        name=name,
        commands=command_names,
        registry=registry,
        **kwargs,
    )
ensure_parent_groups
ensure_parent_groups(
    parent_path: str, registry: Registry
) -> None

Ensure all parent groups in path exist.

Parameters:

Name Type Description Default
parent_path str

Dot-notation path (e.g., "db.migrate")

required
registry Registry

Command registry to update

required
Source code in provide/foundation/cli/click/adapter.py
def ensure_parent_groups(self, parent_path: str, registry: Registry) -> None:
    """Ensure all parent groups in path exist.

    Args:
        parent_path: Dot-notation path (e.g., "db.migrate")
        registry: Command registry to update

    """
    ensure_parent_groups(parent_path, registry)

Functions

create_command_group

create_command_group(
    name: str = "cli",
    commands: list[str] | None = None,
    registry: Registry | None = None,
    **kwargs: Any
) -> Group

Create a Click group with registered commands.

Parameters:

Name Type Description Default
name str

Name for the CLI group

'cli'
commands list[str] | None

List of command names to include (None = all)

None
registry Registry | None

Custom registry (defaults to global)

None
**kwargs Any

Additional Click Group options

{}

Returns:

Type Description
Group

Click Group with registered commands

Raises:

Type Description
CLIBuildError

If group creation fails

Example
Register some commands

@register_command("init") def init_cmd(): pass

Create CLI group

cli = create_command_group("myapp")

Run the CLI

if name == "main": cli()

Source code in provide/foundation/cli/click/builder.py
def create_command_group(  # noqa: C901
    name: str = "cli",
    commands: list[str] | None = None,
    registry: Registry | None = None,
    **kwargs: Any,
) -> Group:
    """Create a Click group with registered commands.

    Args:
        name: Name for the CLI group
        commands: List of command names to include (None = all)
        registry: Custom registry (defaults to global)
        **kwargs: Additional Click Group options

    Returns:
        Click Group with registered commands

    Raises:
        CLIBuildError: If group creation fails

    Example:
        >>> # Register some commands
        >>> @register_command("init")
        >>> def init_cmd():
        >>>     pass
        >>>
        >>> # Create CLI group
        >>> cli = create_command_group("myapp")
        >>>
        >>> # Run the CLI
        >>> if __name__ == "__main__":
        >>>     cli()

    """
    from provide.foundation.hub.registry import get_command_registry

    reg = registry or get_command_registry()

    try:
        group = click.Group(name=name, **kwargs)
        groups: dict[str, Group] = {}

        # Get commands to include
        if commands is None:
            commands = reg.list_dimension(ComponentCategory.COMMAND.value)

        # Sort commands to ensure parents are created before children
        sorted_commands = sorted(commands, key=lambda x: x.count("."))

        # First pass: create all groups
        for cmd_name in sorted_commands:
            entry = reg.get_entry(cmd_name, dimension=ComponentCategory.COMMAND.value)
            if should_skip_entry(entry):
                continue

            # Check if this is a group
            if entry and entry.metadata.get("is_group"):
                create_subgroup(cmd_name, entry, groups, group)

        # Second pass: add commands to groups
        for cmd_name in sorted_commands:
            entry = reg.get_entry(cmd_name, dimension=ComponentCategory.COMMAND.value)
            if should_skip_entry(entry) or should_skip_command(entry):
                continue

            if entry is not None:
                info = entry.metadata.get("info")
                if info:
                    add_command_to_group(info, groups, group, reg)

        return group

    except Exception as e:
        if isinstance(e, CLIBuildError):
            raise
        raise CLIBuildError(
            f"Failed to create Click command group '{name}': {e}",
            group_name=name,
            cause=e,
        ) from e

ensure_parent_groups

ensure_parent_groups(
    parent_path: str, registry: Registry
) -> None

Ensure all parent groups in the path exist, creating them if needed.

Parameters:

Name Type Description Default
parent_path str

Dot-notation path (e.g., "db.migrate")

required
registry Registry

Command registry to update

required
Source code in provide/foundation/cli/click/hierarchy.py
def ensure_parent_groups(parent_path: str, registry: Registry) -> None:
    """Ensure all parent groups in the path exist, creating them if needed.

    Args:
        parent_path: Dot-notation path (e.g., "db.migrate")
        registry: Command registry to update

    """
    parts = parent_path.split(".")

    # Build up the path progressively
    for i in range(len(parts)):
        group_path = ".".join(parts[: i + 1])
        registry_key = group_path

        # Check if this group already exists
        if not registry.get_entry(registry_key, dimension=ComponentCategory.COMMAND.value):
            # Create a placeholder group
            def group_func() -> None:
                """Auto-generated command group."""

            # Set the function name for better debugging
            group_func.__name__ = f"{parts[i]}_group"

            # Register the group
            parent = ".".join(parts[:i]) if i > 0 else None

            from provide.foundation.hub.info import CommandInfo

            info = CommandInfo(
                name=parts[i],
                func=group_func,
                description=f"{parts[i].capitalize()} commands",
                metadata={"is_group": True, "auto_created": True},
                parent=parent,
            )

            registry.register(
                name=registry_key,
                value=group_func,
                dimension=ComponentCategory.COMMAND.value,
                metadata={
                    "info": info,
                    "description": info.description,
                    "parent": parent,
                    "is_group": True,
                    "auto_created": True,
                },
            )

            logger.debug(f"Auto-created group: {group_path}")  # type: ignore[attr-defined]