1
0
Fork 0
terminaltables/terminaltables3/build.py
Daniel Baumann 07735c967b
Merging upstream version 4.0.0 (Closes: #1095814).
Signed-off-by: Daniel Baumann <daniel@debian.org>
2025-02-12 15:00:49 +01:00

174 lines
5.3 KiB
Python

"""Combine cells into rows."""
from typing import Generator, Iterator, Optional, Sequence, Union
from terminaltables3.width_and_alignment import visible_width
def combine(
line: Union[
Generator[Union[int, str], None, None], Iterator[Optional[Union[int, str]]]
],
left: str,
intersect: Optional[str],
right: str,
) -> Generator[int, None, None]:
"""Zip borders between items in `line`.
e.g. ('l', '1', 'c', '2', 'c', '3', 'r')
:param iter line: List to iterate.
:param left: Left border.
:param intersect: Column separator.
:param right: Right border.
:return: Yields combined objects.
"""
# Yield left border.
if left:
yield left
# Yield items with intersect characters.
if intersect:
try:
for j, i in enumerate(line, start=-len(line) + 1):
yield i
if j:
yield intersect
except TypeError: # Generator.
try:
item = next(line)
except StopIteration: # Was empty all along.
pass
else:
while True:
yield item
try:
peek = next(line)
except StopIteration:
break
yield intersect
item = peek
else:
yield from line
# Yield right border.
if right:
yield right
def build_border(
outer_widths: Sequence[int],
horizontal: str,
left: str,
intersect: str,
right: str,
title: Optional[str] = None,
):
"""Build the top/bottom/middle row. Optionally embed the table title within the border.
Title is hidden if it doesn't fit between the left/right characters/edges.
Example return value:
('<', '-----', '+', '------', '+', '-------', '>')
('<', 'My Table', '----', '+', '------->')
:param iter outer_widths: List of widths (with padding) for each column.
:param str horizontal: Character to stretch across each column.
:param str left: Left border.
:param str intersect: Column separator.
:param str right: Right border.
:param title: Overlay the title on the border between the left and right characters.
:return: Returns a generator of strings representing a border.
:rtype: iter
"""
length = 0
# Hide title if it doesn't fit.
if title is not None and outer_widths:
try:
length = visible_width(title)
except TypeError:
title = str(title)
length = visible_width(title)
if length > sum(outer_widths) + len(intersect) * (len(outer_widths) - 1):
title = None
# Handle no title.
if title is None or not outer_widths or not horizontal:
return combine((horizontal * c for c in outer_widths), left, intersect, right)
# Handle title fitting in the first column.
if length == outer_widths[0]:
return combine(
[title] + [horizontal * c for c in outer_widths[1:]], left, intersect, right
)
if length < outer_widths[0]:
columns = [title + horizontal * (outer_widths[0] - length)] + [
horizontal * c for c in outer_widths[1:]
]
return combine(columns, left, intersect, right)
# Handle wide titles/narrow columns.
columns_and_intersects = [title]
for width in combine(outer_widths, None, bool(intersect), None):
# If title is taken care of.
if length < 1:
columns_and_intersects.append(
intersect if width is True else horizontal * width
)
# If title's last character overrides an intersect character.
elif width is True and length == 1:
length = 0
# If this is an intersect character that is overridden by the title.
elif width is True:
length -= 1
# If title's last character is within a column.
elif width >= length:
columns_and_intersects[0] += horizontal * (
width - length
) # Append horizontal chars to title.
length = 0
# If remainder of title won't fit in a column.
else:
length -= width
return combine(columns_and_intersects, left, None, right)
def build_row(row, left, center, right):
"""Combine single or multi-lined cells into a single row of list of lists including borders.
Row must already be padded and extended so each cell has the same number of lines.
Example return value:
[
['>', 'Left ', '|', 'Center', '|', 'Right', '<'],
['>', 'Cell1', '|', 'Cell2 ', '|', 'Cell3', '<'],
]
:param iter row: List of cells for one row.
:param str left: Left border.
:param str center: Column separator.
:param str right: Right border.
:return: Yields other generators that yield strings.
:rtype: iter
"""
if not row or not row[0]:
yield combine((), left, center, right)
return
for row_index in range(len(row[0])):
yield combine((c[row_index] for c in row), left, center, right)
def flatten(table):
"""Flatten table data into a single string with newlines.
:param iter table: Padded and bordered table data.
:return: Joined rows/cells.
:rtype: str
"""
return "\n".join("".join(r) for r in table)