Skip to content

Utils

sereto.cli.utils

AliasedGroup

Bases: Group

A click Group subclass that allows for writing aliases and prefixes of any command.

Source code in sereto/cli/utils.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
class AliasedGroup(click.Group):
    """A click Group subclass that allows for writing aliases and prefixes of any command."""

    def get_command(self, ctx: click.core.Context, cmd_name: str) -> click.Command | None:
        """Retrieves the command with the given name.

        If the command is not found, it looks up an explicit command alias or a command prefix.

        Args:
            ctx: The click context object.
            cmd_name: The name of the command to retrieve.

        Returns:
            The command with the given name, or None if no command is found.
        """
        # built-in commands
        rv = click.Group.get_command(self, ctx, cmd_name)
        if rv is not None:
            return rv

        # if command is on the first position (has no context parent), check for explicit alias
        if ctx.parent is None and cmd_name in cli_aliases:
            # look up an explicit command alias
            actual_cmd = cli_aliases[cmd_name]
            return click.Group.get_command(self, ctx, actual_cmd)

        # look up a command prefix
        matches = [x for x in self.list_commands(ctx) if x.lower().startswith(cmd_name.lower())]
        if not matches:
            return None
        elif len(matches) == 1:
            return click.Group.get_command(self, ctx, matches[0])
        ctx.fail(f"Too many matches: {', '.join(sorted(matches))}")

    def resolve_command(
        self, ctx: click.core.Context, args: list[str]
    ) -> tuple[str | None, click.Command | None, list[str]]:
        """Resolves the full command's name."""
        _, cmd, args = super().resolve_command(ctx, args)
        if cmd is None:
            ctx.fail("No such command")
        return cmd.name, cmd, args

get_command(ctx, cmd_name)

Retrieves the command with the given name.

If the command is not found, it looks up an explicit command alias or a command prefix.

Parameters:

Name Type Description Default
ctx Context

The click context object.

required
cmd_name str

The name of the command to retrieve.

required

Returns:

Type Description
Command | None

The command with the given name, or None if no command is found.

Source code in sereto/cli/utils.py
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
def get_command(self, ctx: click.core.Context, cmd_name: str) -> click.Command | None:
    """Retrieves the command with the given name.

    If the command is not found, it looks up an explicit command alias or a command prefix.

    Args:
        ctx: The click context object.
        cmd_name: The name of the command to retrieve.

    Returns:
        The command with the given name, or None if no command is found.
    """
    # built-in commands
    rv = click.Group.get_command(self, ctx, cmd_name)
    if rv is not None:
        return rv

    # if command is on the first position (has no context parent), check for explicit alias
    if ctx.parent is None and cmd_name in cli_aliases:
        # look up an explicit command alias
        actual_cmd = cli_aliases[cmd_name]
        return click.Group.get_command(self, ctx, actual_cmd)

    # look up a command prefix
    matches = [x for x in self.list_commands(ctx) if x.lower().startswith(cmd_name.lower())]
    if not matches:
        return None
    elif len(matches) == 1:
        return click.Group.get_command(self, ctx, matches[0])
    ctx.fail(f"Too many matches: {', '.join(sorted(matches))}")

resolve_command(ctx, args)

Resolves the full command's name.

Source code in sereto/cli/utils.py
76
77
78
79
80
81
82
83
def resolve_command(
    self, ctx: click.core.Context, args: list[str]
) -> tuple[str | None, click.Command | None, list[str]]:
    """Resolves the full command's name."""
    _, cmd, args = super().resolve_command(ctx, args)
    if cmd is None:
        ctx.fail("No such command")
    return cmd.name, cmd, args

Console

Bases: Console

Singleton wrapper around Rich's Console.

Source code in sereto/cli/utils.py
97
98
class Console(RichConsole, metaclass=Singleton):
    """Singleton wrapper around Rich's Console."""

guard_ni_only_options(click_ctx, non_interactive, ni_only)

Validate that NI-only options are not provided without -N/--non-interactive.

Raises an error when any option that only applies in non-interactive mode is explicitly passed on the command line without the -N/--non-interactive flag.

Parameters:

Name Type Description Default
click_ctx Context

The current Click context (from click.get_current_context()).

required
non_interactive bool

Value of the -N/--non-interactive flag.

required
ni_only dict[str, str]

Mapping of Python parameter name → CLI flag name for options that only apply in non-interactive mode.

required
Source code in sereto/cli/utils.py
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
def guard_ni_only_options(
    click_ctx: click.Context,
    non_interactive: bool,
    ni_only: dict[str, str],
) -> None:
    """Validate that NI-only options are not provided without -N/--non-interactive.

    Raises an error when any option that only applies in non-interactive mode is explicitly
    passed on the command line without the -N/--non-interactive flag.

    Args:
        click_ctx: The current Click context (from ``click.get_current_context()``).
        non_interactive: Value of the -N/--non-interactive flag.
        ni_only: Mapping of Python parameter name → CLI flag name for options that only
            apply in non-interactive mode.
    """
    if non_interactive:
        return

    ni_options = [
        option
        for param, option in ni_only.items()
        if click_ctx.get_parameter_source(param) == click.ParameterSource.COMMANDLINE
    ]
    if ni_options:
        from sereto.exceptions import SeretoValueError

        options = ", ".join(ni_options)
        raise SeretoValueError(f"Options {options} require -N/--non-interactive flag.")

load_enum(enum, message)

Let user select a value from enum.

Source code in sereto/cli/utils.py
86
87
88
89
90
91
92
93
94
def load_enum[E: Enum](enum: type[E], message: str) -> E:
    """Let user select a value from enum."""
    choice = radiolist_dialog(
        title="Select value",
        text=message,
        values=[(e.name, e.value) for e in enum],
    ).run()

    return enum(choice)