diff --git a/kiwi_scp/commands/cli.py b/kiwi_scp/commands/cli.py index 165f1b4..60f4323 100644 --- a/kiwi_scp/commands/cli.py +++ b/kiwi_scp/commands/cli.py @@ -1,4 +1,6 @@ import os +from enum import Enum, auto +from typing import List import click @@ -24,7 +26,28 @@ class KiwiCLI(click.MultiCommand): mod = __import__(f"kiwi_scp.commands.cmd_{name}", None, None, ["cmd"]) except ImportError: return - return mod.cmd + return mod.CMD + + +class KiwiCommand: + @classmethod + def run_for_instance(cls, instance: Instance, **kwargs): + for project in instance.config.projects: + cls.run_for_project(instance, project.name, **kwargs) + + @classmethod + def run_for_project(cls, instance: Instance, project_name: str, **kwargs): + service_names = [service.name for service in instance.get_services(project_name, None).content] + cls.run_for_services(instance, project_name, service_names, **kwargs) + + @classmethod + def run_for_services(cls, instance: Instance, project_name: str, services: List[str], **kwargs): + pass + + +class KiwiCommandType(Enum): + INSTANCE = auto() + PROJECT = auto() + SERVICE = auto() -pass_instance = click.make_pass_decorator(Instance, ensure=True) diff --git a/kiwi_scp/commands/cmd_init.py b/kiwi_scp/commands/cmd_init.py index 69a322e..8843427 100644 --- a/kiwi_scp/commands/cmd_init.py +++ b/kiwi_scp/commands/cmd_init.py @@ -5,7 +5,7 @@ from pathlib import Path import click -from .cli import pass_instance +from .decorators import _pass_instance as pass_instance from .._constants import KIWI_CONF_NAME from ..config import KiwiConfig from ..instance import Instance @@ -37,7 +37,7 @@ from ..misc import user_query help=f"show effective {KIWI_CONF_NAME} contents instead", ) @pass_instance -def cmd(ctx: Instance, output: Path, force: bool, show: bool): +def CMD(ctx: Instance, output: Path, force: bool, show: bool): """Initialize or reconfigure a kiwi-scp instance""" if output is not None: diff --git a/kiwi_scp/commands/cmd_list.py b/kiwi_scp/commands/cmd_list.py index 29a3d8f..dcf9140 100644 --- a/kiwi_scp/commands/cmd_list.py +++ b/kiwi_scp/commands/cmd_list.py @@ -1,63 +1,9 @@ -import typing as t -from typing import Tuple - import click -from .cli import pass_instance +from .cli import KiwiCommandType, KiwiCommand +from .decorators import kiwi_command from ..config import ProjectConfig from ..instance import Instance, Services -from ..misc import service_command - - -class KiwiCommand: - @classmethod - def run_for_instance(cls, instance: Instance, **kwargs): - for project in instance.config.projects: - cls.run_for_project(instance, project, **kwargs) - - @classmethod - def run_for_project(cls, instance: Instance, project: ProjectConfig, **kwargs): - cls.run_for_services(instance, project, instance.get_services(project.name, None), **kwargs) - - @classmethod - def run_for_services(cls, instance: Instance, project: ProjectConfig, services: Services, **kwargs): - pass - - -def kiwi_command( - name: str, - **kwargs, -) -> t.Callable: - def decorator(command_cls: t.Type[KiwiCommand]) -> t.Callable: - - @click.command(name, **kwargs) - @pass_instance - @service_command - def cmd(ctx: Instance, project: t.Optional[str], services: Tuple[str], **cmd_kwargs) -> None: - print(f"{ctx.directory!r}: {project!r}, {services!r}") - if project is None: - # run for whole instance - print(f"for instance: {cmd_kwargs}") - command_cls.run_for_instance(ctx, **cmd_kwargs) - - elif not services: - # run for one entire project - print(f"for project {project}: {cmd_kwargs}") - for project_cfg in ctx.config.projects: - if project_cfg.name == project: - command_cls.run_for_project(ctx, project_cfg, **cmd_kwargs) - - else: - # run for some services - print(f"for services {project}.{services}: {cmd_kwargs}") - for project_cfg in ctx.config.projects: - if project_cfg.name == project: - services = ctx.get_services(project_cfg.name, services) - command_cls.run_for_services(ctx, project_cfg, services, **cmd_kwargs) - - return cmd - - return decorator @click.option( @@ -67,9 +13,10 @@ def kiwi_command( ) @kiwi_command( "list", + KiwiCommandType.PROJECT, short_help="Inspect a kiwi-scp instance", ) -class cmd(KiwiCommand): +class CMD(KiwiCommand): @classmethod def run_for_instance(cls, instance: Instance, show: bool = None, **kwargs): print(show) @@ -80,17 +27,3 @@ class cmd(KiwiCommand): **kwargs): print(show) print(services) - -# @click.command( -# "list", -# short_help="Inspect a kiwi-scp instance", -# ) -# @pass_instance -# @service_command -# def cmd(ctx: Instance, project: str, services: Tuple[str]): -# """List projects in this instance, services inside a project or service(s) inside a project""" -# print(f"project: {project!r}, services: {services!r}") -# if project is not None: -# print(ctx.get_services(project, services)) -# else: -# print(f"projects: {ctx.config.projects}") diff --git a/kiwi_scp/commands/decorators.py b/kiwi_scp/commands/decorators.py new file mode 100644 index 0000000..7968992 --- /dev/null +++ b/kiwi_scp/commands/decorators.py @@ -0,0 +1,61 @@ +from typing import Callable, Type, Optional, Tuple + +import click + +from .cli import KiwiCommandType, KiwiCommand +from ..instance import Instance + +_pass_instance = click.make_pass_decorator( + Instance, + ensure=True, +) +_project_arg = click.argument( + "project", + required=False, + type=str, +) +_services_arg = click.argument( + "services", + metavar="[SERVICE]...", + nargs=-1, + type=str, +) + + +def kiwi_command( + name: str, + command_type: KiwiCommandType, + **kwargs, +) -> Callable: + def decorator(command_cls: Type[KiwiCommand]) -> Callable: + + @click.command(name, **kwargs) + @_pass_instance + def cmd(ctx: Instance, project: Optional[str] = None, services: Optional[Tuple[str]] = None, + **cmd_kwargs) -> None: + print(f"{ctx.directory!r}: {project!r}, {services!r}") + if project is None: + # run for whole instance + print(f"for instance: {cmd_kwargs}") + command_cls.run_for_instance(ctx, **cmd_kwargs) + + elif not services: + # run for one entire project + print(f"for project {project}: {cmd_kwargs}") + command_cls.run_for_project(ctx, project, **cmd_kwargs) + + else: + # run for some services + print(f"for services {project}.{services}: {cmd_kwargs}") + command_cls.run_for_services(ctx, project, list(services), **cmd_kwargs) + + if command_type is KiwiCommandType.PROJECT: + cmd = _project_arg(cmd) + + elif command_type is KiwiCommandType.SERVICE: + cmd = _project_arg(cmd) + cmd = _services_arg(cmd) + + return cmd + + return decorator \ No newline at end of file diff --git a/kiwi_scp/misc.py b/kiwi_scp/misc.py index dccc434..de07027 100644 --- a/kiwi_scp/misc.py +++ b/kiwi_scp/misc.py @@ -1,44 +1,13 @@ import re -from typing import Any, Type, List, Callable, Optional +from typing import Any, Type, Optional -import attr import click import ruamel.yaml import ruamel.yaml.compat -from click.decorators import FC from ._constants import HEADER_KIWI_CONF_NAME -@attr.s -class _MultiDecorator: - options: List[Callable[[FC], FC]] = attr.ib(factory=list) - - def __call__(self, target: FC): - for option in reversed(self.options): - target = option(target) - - return target - - -_project_arg = click.argument( - "project", - required=False, - type=str, -) - -_services_arg = click.argument( - "services", - metavar="[SERVICE]...", - nargs=-1, - type=str, -) - -instance_command = _MultiDecorator([]) -project_command = _MultiDecorator([_project_arg]) -service_command = _MultiDecorator([_project_arg, _services_arg]) - - def user_query(description: str, default: Any, cast_to: Type[Any] = str): # prompt user as per argument while True: