Skip to content

Cli

zenml.cli.cli

TagGroup (Group)

Override the default click Group to add a tag. The tag is used to group commands and groups of commands in the help output.

Source code in zenml/cli/cli.py
class TagGroup(click.Group):
    """
    Override the default click Group to add a tag.
    The tag is used to group commands and groups of
    commands in the help output.
    """

    def __init__(
        self,
        name: Optional[str] = None,
        tag: Optional[CliCategories] = None,
        commands: Optional[
            Union[Dict[str, click.Command], Sequence[click.Command]]
        ] = None,
        **kwargs: Dict[str, Any],
    ) -> None:
        super(TagGroup, self).__init__(name, commands, **kwargs)
        self.tag = tag or CliCategories.OTHER_COMMANDS

ZenContext (Context)

Override the default click Context to add the new Formatter.

Source code in zenml/cli/cli.py
class ZenContext(click.Context):
    """
    Override the default click Context to add the new Formatter.
    """

    formatter_class = ZenFormatter

formatter_class (HelpFormatter)

Override the default HelpFormatter to add a custom format for the help command output.

Source code in zenml/cli/cli.py
class ZenFormatter(formatting.HelpFormatter):
    """
    Override the default HelpFormatter to add a custom
    format for the help command output.
    """

    def __init__(
        self,
        indent_increment: int = 2,
        width: Optional[int] = None,
        max_width: Optional[int] = None,
    ) -> None:
        super(ZenFormatter, self).__init__(indent_increment, width, max_width)
        self.current_indent = 0

    def write_dl(
        self,
        rows: Sequence[Tuple[str, ...]],
        col_max: int = 30,
        col_spacing: int = 2,
    ) -> None:
        """Writes a definition list into the buffer.  This is how options
        and commands are usually formatted.

        Arguments:
            rows: a list of items as tuples for the terms and values.
            col_max: the maximum width of the first column.
            col_spacing: the number of spaces between the first and
                            second column (and third).

        The default behavior is to format the rows in a definition list
        with rows of 2 columns following the format ``(term, value)``.
        But for new CLI commands, we want to format the rows in a definition
        list with rows of 3 columns following the format the format
                            ``(term, value, description)``.
        """
        rows = list(rows)
        widths = measure_table(rows)

        if len(widths) == 2:
            first_col = min(widths[0], col_max) + col_spacing

            for first, second in iter_rows(rows, len(widths)):
                self.write(f"{'':>{self.current_indent}}{first}")
                if not second:
                    self.write("\n")
                    continue
                if term_len(first) <= first_col - col_spacing:
                    self.write(" " * (first_col - term_len(first)))
                else:
                    self.write("\n")
                    self.write(" " * (first_col + self.current_indent))

                text_width = max(self.width - first_col - 2, 10)
                wrapped_text = formatting.wrap_text(
                    second, text_width, preserve_paragraphs=True
                )
                lines = wrapped_text.splitlines()

                if lines:
                    self.write(f"{lines[0]}\n")

                    for line in lines[1:]:
                        self.write(
                            f"{'':>{first_col + self.current_indent}}{line}\n"
                        )
                else:
                    self.write("\n")

        elif len(widths) == 3:

            first_col = min(widths[0], col_max) + col_spacing
            second_col = min(widths[1], col_max) + col_spacing * 2

            current_tag = None
            for (first, second, third) in iter_rows(rows, len(widths)):
                if current_tag != first:
                    current_tag = first
                    self.write("\n")
                    # Adding [#431d93] [/#431d93] makes the tag colorful when it is printed by rich print
                    self.write(
                        f"[#431d93]{'':>{self.current_indent}}{first}:[/#431d93]\n"
                    )

                if not third:
                    self.write("\n")
                    continue

                if term_len(first) <= first_col - col_spacing:
                    self.write(" " * self.current_indent * 2)
                else:
                    self.write("\n")
                    self.write(" " * (first_col + self.current_indent))

                self.write(f"{'':>{self.current_indent}}{second}")

                text_width = max(self.width - second_col - 4, 10)
                wrapped_text = formatting.wrap_text(
                    third, text_width, preserve_paragraphs=True
                )
                lines = wrapped_text.splitlines()

                if lines:
                    self.write(
                        " "
                        * (second_col - term_len(second) + self.current_indent)
                    )
                    self.write(f"{lines[0]}\n")

                    for line in lines[1:]:
                        self.write(
                            f"{'':>{second_col + self.current_indent * 4 }}{line}\n"
                        )
                else:
                    self.write("\n")
        else:
            raise TypeError(
                "Expected either three or two columns for definition list"
            )
write_dl(self, rows, col_max=30, col_spacing=2)

Writes a definition list into the buffer. This is how options and commands are usually formatted.

Parameters:

Name Type Description Default
rows Sequence[Tuple[str, ...]]

a list of items as tuples for the terms and values.

required
col_max int

the maximum width of the first column.

30
col_spacing int

the number of spaces between the first and second column (and third).

2

The default behavior is to format the rows in a definition list with rows of 2 columns following the format (term, value). But for new CLI commands, we want to format the rows in a definition list with rows of 3 columns following the format the format (term, value, description).

Source code in zenml/cli/cli.py
def write_dl(
    self,
    rows: Sequence[Tuple[str, ...]],
    col_max: int = 30,
    col_spacing: int = 2,
) -> None:
    """Writes a definition list into the buffer.  This is how options
    and commands are usually formatted.

    Arguments:
        rows: a list of items as tuples for the terms and values.
        col_max: the maximum width of the first column.
        col_spacing: the number of spaces between the first and
                        second column (and third).

    The default behavior is to format the rows in a definition list
    with rows of 2 columns following the format ``(term, value)``.
    But for new CLI commands, we want to format the rows in a definition
    list with rows of 3 columns following the format the format
                        ``(term, value, description)``.
    """
    rows = list(rows)
    widths = measure_table(rows)

    if len(widths) == 2:
        first_col = min(widths[0], col_max) + col_spacing

        for first, second in iter_rows(rows, len(widths)):
            self.write(f"{'':>{self.current_indent}}{first}")
            if not second:
                self.write("\n")
                continue
            if term_len(first) <= first_col - col_spacing:
                self.write(" " * (first_col - term_len(first)))
            else:
                self.write("\n")
                self.write(" " * (first_col + self.current_indent))

            text_width = max(self.width - first_col - 2, 10)
            wrapped_text = formatting.wrap_text(
                second, text_width, preserve_paragraphs=True
            )
            lines = wrapped_text.splitlines()

            if lines:
                self.write(f"{lines[0]}\n")

                for line in lines[1:]:
                    self.write(
                        f"{'':>{first_col + self.current_indent}}{line}\n"
                    )
            else:
                self.write("\n")

    elif len(widths) == 3:

        first_col = min(widths[0], col_max) + col_spacing
        second_col = min(widths[1], col_max) + col_spacing * 2

        current_tag = None
        for (first, second, third) in iter_rows(rows, len(widths)):
            if current_tag != first:
                current_tag = first
                self.write("\n")
                # Adding [#431d93] [/#431d93] makes the tag colorful when it is printed by rich print
                self.write(
                    f"[#431d93]{'':>{self.current_indent}}{first}:[/#431d93]\n"
                )

            if not third:
                self.write("\n")
                continue

            if term_len(first) <= first_col - col_spacing:
                self.write(" " * self.current_indent * 2)
            else:
                self.write("\n")
                self.write(" " * (first_col + self.current_indent))

            self.write(f"{'':>{self.current_indent}}{second}")

            text_width = max(self.width - second_col - 4, 10)
            wrapped_text = formatting.wrap_text(
                third, text_width, preserve_paragraphs=True
            )
            lines = wrapped_text.splitlines()

            if lines:
                self.write(
                    " "
                    * (second_col - term_len(second) + self.current_indent)
                )
                self.write(f"{lines[0]}\n")

                for line in lines[1:]:
                    self.write(
                        f"{'':>{second_col + self.current_indent * 4 }}{line}\n"
                    )
            else:
                self.write("\n")
    else:
        raise TypeError(
            "Expected either three or two columns for definition list"
        )

ZenMLCLI (Group)

Override the default click Group to create a custom format command help output.

Source code in zenml/cli/cli.py
class ZenMLCLI(click.Group):
    """
    Override the default click Group to create a custom
    format command help output.
    """

    context_class = ZenContext

    def get_help(self, ctx: Context) -> str:
        """Formats the help into a string and returns it.

        Calls :meth:`format_help` internally.
        """
        formatter = ctx.make_formatter()
        self.format_help(ctx, formatter)
        # TODO [ENG-862]: Find solution for support console.pager and color support in print
        rich.print(formatter.getvalue().rstrip("\n"))
        return ""

    def format_commands(
        self, ctx: click.Context, formatter: formatting.HelpFormatter
    ) -> None:
        """
        Extra format methods for multi methods that adds all the commands
        after the options.
        This custom format commands is used to retrive the commands and
        groups of commands with a tag. In order to call the new custom format
        method, the command must be added to the ZenMLCLI class.
        """
        commands: List[Tuple[CliCategories, str, Union[Command, TagGroup]]] = []
        for subcommand in self.list_commands(ctx):
            cmd = self.get_command(ctx, subcommand)
            # What is this, the tool lied about a command.  Ignore it
            if cmd is None or cmd.hidden:
                continue
            category = (
                cmd.tag
                if isinstance(cmd, TagGroup)
                else CliCategories.OTHER_COMMANDS
            )
            commands.append(
                (
                    category,
                    subcommand,
                    cmd,
                )
            )

        if len(commands):
            ordered_categories = list(CliCategories.__members__.values())
            commands = list(
                sorted(
                    (commands),
                    key=lambda x: (
                        ordered_categories.index(x[0]),
                        x[0].value,
                        x[1],
                    ),
                )
            )
            rows: List[Tuple[str, str, str]] = []
            for (tag, subcommand, cmd) in commands:
                help = cmd.get_short_help_str(limit=formatter.width)
                rows.append((tag.value, subcommand, help))
            if rows:
                colored_section_title = (
                    "[dim cyan]Available ZenML Commands (grouped)[/dim cyan]"
                )
                with formatter.section(colored_section_title):
                    formatter.write_dl(rows)  # type: ignore[arg-type]

context_class (Context)

Override the default click Context to add the new Formatter.

Source code in zenml/cli/cli.py
class ZenContext(click.Context):
    """
    Override the default click Context to add the new Formatter.
    """

    formatter_class = ZenFormatter
formatter_class (HelpFormatter)

Override the default HelpFormatter to add a custom format for the help command output.

Source code in zenml/cli/cli.py
class ZenFormatter(formatting.HelpFormatter):
    """
    Override the default HelpFormatter to add a custom
    format for the help command output.
    """

    def __init__(
        self,
        indent_increment: int = 2,
        width: Optional[int] = None,
        max_width: Optional[int] = None,
    ) -> None:
        super(ZenFormatter, self).__init__(indent_increment, width, max_width)
        self.current_indent = 0

    def write_dl(
        self,
        rows: Sequence[Tuple[str, ...]],
        col_max: int = 30,
        col_spacing: int = 2,
    ) -> None:
        """Writes a definition list into the buffer.  This is how options
        and commands are usually formatted.

        Arguments:
            rows: a list of items as tuples for the terms and values.
            col_max: the maximum width of the first column.
            col_spacing: the number of spaces between the first and
                            second column (and third).

        The default behavior is to format the rows in a definition list
        with rows of 2 columns following the format ``(term, value)``.
        But for new CLI commands, we want to format the rows in a definition
        list with rows of 3 columns following the format the format
                            ``(term, value, description)``.
        """
        rows = list(rows)
        widths = measure_table(rows)

        if len(widths) == 2:
            first_col = min(widths[0], col_max) + col_spacing

            for first, second in iter_rows(rows, len(widths)):
                self.write(f"{'':>{self.current_indent}}{first}")
                if not second:
                    self.write("\n")
                    continue
                if term_len(first) <= first_col - col_spacing:
                    self.write(" " * (first_col - term_len(first)))
                else:
                    self.write("\n")
                    self.write(" " * (first_col + self.current_indent))

                text_width = max(self.width - first_col - 2, 10)
                wrapped_text = formatting.wrap_text(
                    second, text_width, preserve_paragraphs=True
                )
                lines = wrapped_text.splitlines()

                if lines:
                    self.write(f"{lines[0]}\n")

                    for line in lines[1:]:
                        self.write(
                            f"{'':>{first_col + self.current_indent}}{line}\n"
                        )
                else:
                    self.write("\n")

        elif len(widths) == 3:

            first_col = min(widths[0], col_max) + col_spacing
            second_col = min(widths[1], col_max) + col_spacing * 2

            current_tag = None
            for (first, second, third) in iter_rows(rows, len(widths)):
                if current_tag != first:
                    current_tag = first
                    self.write("\n")
                    # Adding [#431d93] [/#431d93] makes the tag colorful when it is printed by rich print
                    self.write(
                        f"[#431d93]{'':>{self.current_indent}}{first}:[/#431d93]\n"
                    )

                if not third:
                    self.write("\n")
                    continue

                if term_len(first) <= first_col - col_spacing:
                    self.write(" " * self.current_indent * 2)
                else:
                    self.write("\n")
                    self.write(" " * (first_col + self.current_indent))

                self.write(f"{'':>{self.current_indent}}{second}")

                text_width = max(self.width - second_col - 4, 10)
                wrapped_text = formatting.wrap_text(
                    third, text_width, preserve_paragraphs=True
                )
                lines = wrapped_text.splitlines()

                if lines:
                    self.write(
                        " "
                        * (second_col - term_len(second) + self.current_indent)
                    )
                    self.write(f"{lines[0]}\n")

                    for line in lines[1:]:
                        self.write(
                            f"{'':>{second_col + self.current_indent * 4 }}{line}\n"
                        )
                else:
                    self.write("\n")
        else:
            raise TypeError(
                "Expected either three or two columns for definition list"
            )
write_dl(self, rows, col_max=30, col_spacing=2)

Writes a definition list into the buffer. This is how options and commands are usually formatted.

Parameters:

Name Type Description Default
rows Sequence[Tuple[str, ...]]

a list of items as tuples for the terms and values.

required
col_max int

the maximum width of the first column.

30
col_spacing int

the number of spaces between the first and second column (and third).

2

The default behavior is to format the rows in a definition list with rows of 2 columns following the format (term, value). But for new CLI commands, we want to format the rows in a definition list with rows of 3 columns following the format the format (term, value, description).

Source code in zenml/cli/cli.py
def write_dl(
    self,
    rows: Sequence[Tuple[str, ...]],
    col_max: int = 30,
    col_spacing: int = 2,
) -> None:
    """Writes a definition list into the buffer.  This is how options
    and commands are usually formatted.

    Arguments:
        rows: a list of items as tuples for the terms and values.
        col_max: the maximum width of the first column.
        col_spacing: the number of spaces between the first and
                        second column (and third).

    The default behavior is to format the rows in a definition list
    with rows of 2 columns following the format ``(term, value)``.
    But for new CLI commands, we want to format the rows in a definition
    list with rows of 3 columns following the format the format
                        ``(term, value, description)``.
    """
    rows = list(rows)
    widths = measure_table(rows)

    if len(widths) == 2:
        first_col = min(widths[0], col_max) + col_spacing

        for first, second in iter_rows(rows, len(widths)):
            self.write(f"{'':>{self.current_indent}}{first}")
            if not second:
                self.write("\n")
                continue
            if term_len(first) <= first_col - col_spacing:
                self.write(" " * (first_col - term_len(first)))
            else:
                self.write("\n")
                self.write(" " * (first_col + self.current_indent))

            text_width = max(self.width - first_col - 2, 10)
            wrapped_text = formatting.wrap_text(
                second, text_width, preserve_paragraphs=True
            )
            lines = wrapped_text.splitlines()

            if lines:
                self.write(f"{lines[0]}\n")

                for line in lines[1:]:
                    self.write(
                        f"{'':>{first_col + self.current_indent}}{line}\n"
                    )
            else:
                self.write("\n")

    elif len(widths) == 3:

        first_col = min(widths[0], col_max) + col_spacing
        second_col = min(widths[1], col_max) + col_spacing * 2

        current_tag = None
        for (first, second, third) in iter_rows(rows, len(widths)):
            if current_tag != first:
                current_tag = first
                self.write("\n")
                # Adding [#431d93] [/#431d93] makes the tag colorful when it is printed by rich print
                self.write(
                    f"[#431d93]{'':>{self.current_indent}}{first}:[/#431d93]\n"
                )

            if not third:
                self.write("\n")
                continue

            if term_len(first) <= first_col - col_spacing:
                self.write(" " * self.current_indent * 2)
            else:
                self.write("\n")
                self.write(" " * (first_col + self.current_indent))

            self.write(f"{'':>{self.current_indent}}{second}")

            text_width = max(self.width - second_col - 4, 10)
            wrapped_text = formatting.wrap_text(
                third, text_width, preserve_paragraphs=True
            )
            lines = wrapped_text.splitlines()

            if lines:
                self.write(
                    " "
                    * (second_col - term_len(second) + self.current_indent)
                )
                self.write(f"{lines[0]}\n")

                for line in lines[1:]:
                    self.write(
                        f"{'':>{second_col + self.current_indent * 4 }}{line}\n"
                    )
            else:
                self.write("\n")
    else:
        raise TypeError(
            "Expected either three or two columns for definition list"
        )

format_commands(self, ctx, formatter)

Extra format methods for multi methods that adds all the commands after the options. This custom format commands is used to retrive the commands and groups of commands with a tag. In order to call the new custom format method, the command must be added to the ZenMLCLI class.

Source code in zenml/cli/cli.py
def format_commands(
    self, ctx: click.Context, formatter: formatting.HelpFormatter
) -> None:
    """
    Extra format methods for multi methods that adds all the commands
    after the options.
    This custom format commands is used to retrive the commands and
    groups of commands with a tag. In order to call the new custom format
    method, the command must be added to the ZenMLCLI class.
    """
    commands: List[Tuple[CliCategories, str, Union[Command, TagGroup]]] = []
    for subcommand in self.list_commands(ctx):
        cmd = self.get_command(ctx, subcommand)
        # What is this, the tool lied about a command.  Ignore it
        if cmd is None or cmd.hidden:
            continue
        category = (
            cmd.tag
            if isinstance(cmd, TagGroup)
            else CliCategories.OTHER_COMMANDS
        )
        commands.append(
            (
                category,
                subcommand,
                cmd,
            )
        )

    if len(commands):
        ordered_categories = list(CliCategories.__members__.values())
        commands = list(
            sorted(
                (commands),
                key=lambda x: (
                    ordered_categories.index(x[0]),
                    x[0].value,
                    x[1],
                ),
            )
        )
        rows: List[Tuple[str, str, str]] = []
        for (tag, subcommand, cmd) in commands:
            help = cmd.get_short_help_str(limit=formatter.width)
            rows.append((tag.value, subcommand, help))
        if rows:
            colored_section_title = (
                "[dim cyan]Available ZenML Commands (grouped)[/dim cyan]"
            )
            with formatter.section(colored_section_title):
                formatter.write_dl(rows)  # type: ignore[arg-type]

get_help(self, ctx)

Formats the help into a string and returns it.

Calls :meth:format_help internally.

Source code in zenml/cli/cli.py
def get_help(self, ctx: Context) -> str:
    """Formats the help into a string and returns it.

    Calls :meth:`format_help` internally.
    """
    formatter = ctx.make_formatter()
    self.format_help(ctx, formatter)
    # TODO [ENG-862]: Find solution for support console.pager and color support in print
    rich.print(formatter.getvalue().rstrip("\n"))
    return ""