Skip to content

Formatter

zenml.cli.formatter

Helper functions to format output for CLI.

ZenFormatter (HelpFormatter)

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

Source code in zenml/cli/formatter.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:
        """Initialize the formatter.

        Args:
            indent_increment: The number of spaces to indent each level of
                nesting.
            width: The maximum width of the help output.
            max_width: The maximum width of the help output.
        """
        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)``.

        Raises:
            TypeError: if the number of columns is not 2 or 3.
        """
        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"
            )

__init__(self, indent_increment=2, width=None, max_width=None) special

Initialize the formatter.

Parameters:

Name Type Description Default
indent_increment int

The number of spaces to indent each level of nesting.

2
width Optional[int]

The maximum width of the help output.

None
max_width Optional[int]

The maximum width of the help output.

None
Source code in zenml/cli/formatter.py
def __init__(
    self,
    indent_increment: int = 2,
    width: Optional[int] = None,
    max_width: Optional[int] = None,
) -> None:
    """Initialize the formatter.

    Args:
        indent_increment: The number of spaces to indent each level of
            nesting.
        width: The maximum width of the help output.
        max_width: The maximum width of the help output.
    """
    super(ZenFormatter, self).__init__(indent_increment, width, max_width)
    self.current_indent = 0

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).

Exceptions:

Type Description
TypeError

if the number of columns is not 2 or 3.

Source code in zenml/cli/formatter.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)``.

    Raises:
        TypeError: if the number of columns is not 2 or 3.
    """
    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"
        )

iter_rows(rows, col_count)

Iterate over rows of a table.

Parameters:

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

The rows of the table.

required
col_count int

The number of columns in the table.

required

Yields:

Type Description
Iterator[Tuple[str, ...]]

An iterator over the rows of the table.

Source code in zenml/cli/formatter.py
def iter_rows(
    rows: Iterable[Tuple[str, ...]],
    col_count: int,
) -> Iterator[Tuple[str, ...]]:
    """Iterate over rows of a table.

    Args:
        rows: The rows of the table.
        col_count: The number of columns in the table.

    Yields:
        An iterator over the rows of the table.
    """
    for row in rows:
        yield row + ("",) * (col_count - len(row))

measure_table(rows)

Measure the width of each column in a table.

Parameters:

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

The rows of the table.

required

Returns:

Type Description
Tuple[int, ...]

A tuple of the width of each column.

Source code in zenml/cli/formatter.py
def measure_table(rows: Iterable[Tuple[str, ...]]) -> Tuple[int, ...]:
    """Measure the width of each column in a table.

    Args:
        rows: The rows of the table.

    Returns:
        A tuple of the width of each column.
    """
    widths: Dict[int, int] = {}
    for row in rows:
        for idx, col in enumerate(row):
            widths[idx] = max(widths.get(idx, 0), term_len(col))

    return tuple(y for x, y in sorted(widths.items()))