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