.. module:: CLI CLI ========================= The CLI module provides utilities and extensions for building command-line interfaces using the Click library. AliasedGroup ----------------------------------------------------------------------------------- The ``AliasedGroup`` class extends Click's ``Group`` class to support command aliases, allowing users to invoke commands using alternative names. This is particularly useful for: - Creating shortcuts for frequently used commands - Maintaining backward compatibility when renaming commands - Supporting multiple naming conventions Basic Usage ^^^^^^^^^^^ Here's a simple example of using ``AliasedGroup``: .. code-block:: python from core_extensions.cli import AliasedGroup cli = AliasedGroup() @cli.command("deploy", aliases=["release", "publish"]) def deploy_command(): """Deploy the application""" print("Deploying application...") if __name__ == "__main__": cli() Now users can run any of these commands: .. code-block:: bash # All of these are equivalent $ python app.py deploy $ python app.py release $ python app.py publish Subgroup Aliases ^^^^^^^^^^^^^^^^ The ``group()`` method supports aliases in the same way as ``command()``, allowing subgroups to be reachable via alternative names: .. code-block:: python from core_extensions.cli import AliasedGroup cli = AliasedGroup() @cli.group("infra", aliases=["i", "infrastructure"]) def infra_group(): """Infrastructure commands""" @infra_group.command("status") def infra_status(): """Show infrastructure status""" print("All systems operational") if __name__ == "__main__": cli() .. code-block:: bash # All of these are equivalent $ python app.py infra status $ python app.py i status $ python app.py infrastructure status .. note:: By default, the created subgroup is a plain ``click.Group`` instance. Its own commands do **not** support the ``aliases`` parameter. To enable full alias support at every nesting level, pass ``cls=AliasedGroup`` explicitly when declaring the subgroup: .. code-block:: python @cli.group("db", aliases=["database"], cls=AliasedGroup) def db_group(): """Database commands""" @db_group.command("migrate", aliases=["m"]) def db_migrate(): """Run pending migrations""" print("Migrations applied.") .. code-block:: bash # Both reach the same command $ python app.py db migrate $ python app.py db m Help Output ^^^^^^^^^^^ Aliases are shown automatically inline in ``--help`` output, so users can discover them without reading source code: .. code-block:: bash $ python app.py --help .. code-block:: text Usage: app.py [OPTIONS] COMMAND [ARGS]... Options: --help Show this message and exit. Commands: deploy Deploy the application (aliases: release, publish) infra Infrastructure commands (aliases: i, infrastructure) Commands without aliases are displayed normally, without any annotation. Alias Collision Protection ^^^^^^^^^^^^^^^^^^^^^^^^^^ Registering the same alias on two different commands raises ``ValueError`` immediately at decoration time: .. code-block:: python cli = AliasedGroup() @cli.command("deploy", aliases=["release"]) def deploy(): pass @cli.command("rollback", aliases=["release"]) # raises ValueError def rollback(): pass .. code-block:: text ValueError: Alias 'release' is already registered to command 'deploy'. This makes conflicts impossible to miss during development rather than causing silent routing bugs at runtime. Imperative Registration ^^^^^^^^^^^^^^^^^^^^^^^ Commands and subgroups can also be registered imperatively with ``add_command()``, which accepts the same ``aliases`` parameter as the decorator forms: .. code-block:: python import click from core_extensions.cli import AliasedGroup cli = AliasedGroup() @click.command("deploy") def deploy_command(): """Deploy the application""" print("Deploying...") cli.add_command(deploy_command, aliases=["release", "publish"]) An explicit ``name`` override and aliases can be combined; the alias will map to the overridden name rather than the command's own name: .. code-block:: python cli.add_command(deploy_command, name="ship", aliases=["release"]) # "release" -> "ship", not "deploy" The same ``ValueError`` collision guard applies — registering a duplicate alias via ``add_command()`` raises immediately. API Reference ^^^^^^^^^^^^^ .. class:: AliasedGroup A Click Group that supports command aliases. This class extends Click's :class:`click.Group` to allow commands and subgroups to be invoked using alternative names (aliases). .. attribute:: aliases Dictionary mapping alias names to actual command names. :type: dict[str, str] .. method:: command(*args, aliases=None, **kwargs) Register a command with optional aliases. Supports both ``@group.command`` (no parentheses) and ``@group.command("name", aliases=[...])`` usage patterns. :param aliases: List of alternative names for the command. Raises ``ValueError`` if an alias is already registered to another command. :type aliases: list[str] | None :param args: Positional arguments passed to Click's command decorator :param kwargs: Keyword arguments passed to Click's command decorator :return: Decorator function or Command object :rtype: Callable[[Callable], Command] | Command .. method:: group(*args, aliases=None, **kwargs) Register a subgroup with optional aliases. Supports both ``@group.group`` (no parentheses) and ``@group.group("name", aliases=[...])`` usage patterns. :param aliases: List of alternative names for the subgroup. Raises ``ValueError`` if an alias is already registered to another command or group. :type aliases: list[str] | None :param args: Positional arguments passed to Click's group decorator :param kwargs: Keyword arguments passed to Click's group decorator :return: Decorator function or Group object :rtype: Callable[[Callable], Group] | Group .. method:: add_command(cmd, name=None, aliases=None) Register a pre-built command or group, with optional aliases. The ``name`` parameter overrides the command's own name, exactly as in Click's base ``add_command()``. :param cmd: The command or group to register. :type cmd: click.Command :param name: Optional name override for the registered command. :type name: str | None :param aliases: List of alternative names. Raises ``ValueError`` if any alias is already registered to another command. :type aliases: list[str] | None .. method:: format_commands(ctx, formatter) Write the commands section of the help output. Aliases are appended inline to the short help string of each command that has them, in the form ``(aliases: x, y)``. :param ctx: Click context :type ctx: click.Context :param formatter: Help formatter :type formatter: click.HelpFormatter .. method:: get_command(ctx, cmd_name) Retrieve a command by name or alias. :param ctx: Click context :type ctx: click.Context :param cmd_name: Command name or alias :type cmd_name: str :return: Command object if found, None otherwise :rtype: Command | None