116 lines
2.7 KiB
Python
116 lines
2.7 KiB
Python
# -*- coding: utf-8 -*-
|
|
from typing import Collection, Optional, Union
|
|
|
|
C_UL = "┌"
|
|
C_UR = "┐"
|
|
C_LL = "└"
|
|
C_LR = "┘"
|
|
|
|
C_V = "│"
|
|
C_VL = "├"
|
|
C_VR = "┤"
|
|
|
|
C_H = "─"
|
|
C_HD = "┬"
|
|
C_HU = "┴"
|
|
C_X = "┼"
|
|
|
|
|
|
def row(
|
|
fields: Collection[str],
|
|
sep: str = C_V,
|
|
start: str = C_V,
|
|
end: str = C_V,
|
|
) -> str:
|
|
res = []
|
|
|
|
for i, field in enumerate(fields):
|
|
if not i:
|
|
res.append(start)
|
|
else:
|
|
res.append(sep)
|
|
|
|
res.append(field)
|
|
|
|
res.append(end)
|
|
|
|
return "".join(res)
|
|
|
|
|
|
def table(
|
|
data: Collection[Collection[str]],
|
|
headers: Optional[Collection] = None,
|
|
header_interval: Optional[int] = None,
|
|
justify: Optional[Union[str, Collection[str]]] = None,
|
|
collapse_empty: bool = False,
|
|
):
|
|
res = []
|
|
|
|
if not data:
|
|
return ""
|
|
|
|
columns = max([len(x) for x in data])
|
|
if headers:
|
|
columns = max(columns, len(headers))
|
|
|
|
if isinstance(justify, str):
|
|
justify = [justify] * columns
|
|
elif not justify:
|
|
justify = []
|
|
|
|
justify = list(justify) + ["^"] * (columns - len(justify))
|
|
|
|
colwidths = [0] * columns
|
|
|
|
if headers:
|
|
for i, col in enumerate(headers):
|
|
colwidths[i] = max(colwidths[i], len(col))
|
|
|
|
for data_row in data:
|
|
for i, col in enumerate(data_row):
|
|
colwidths[i] = max(colwidths[i], len(col))
|
|
|
|
def row_iterable():
|
|
if headers and not header_interval:
|
|
yield headers
|
|
|
|
for i, data_row in enumerate(data):
|
|
if header_interval and i % header_interval == 0:
|
|
yield headers
|
|
yield data_row
|
|
|
|
for i, data_row in enumerate(row_iterable()):
|
|
if not i:
|
|
res.append(
|
|
row([C_H * colw for colw in colwidths], start=C_UL, sep=C_HD, end=C_UR)
|
|
)
|
|
else:
|
|
res.append(
|
|
row([C_H * colw for colw in colwidths], start=C_VL, sep=C_X, end=C_VR)
|
|
)
|
|
is_empty = not any([True for x in data_row if x.strip()])
|
|
if not (is_empty and collapse_empty):
|
|
padding = [""] * (columns - len(data_row))
|
|
padded_row = [
|
|
f"{cell:{justify[j]}{colwidths[j]}s}"
|
|
for j, cell in enumerate(list(data_row) + padding)
|
|
]
|
|
res.append(row(padded_row))
|
|
|
|
res.append(row([C_H * colw for colw in colwidths], start=C_LL, sep=C_HU, end=C_LR))
|
|
|
|
return "\n".join(res)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
headers = [""] + ["···{:01X}".format(x) for x in range(16)]
|
|
data = [[f"{x:03X}·"] for x in range(32)]
|
|
|
|
print(
|
|
table(
|
|
data,
|
|
headers=headers,
|
|
justify=">",
|
|
header_interval=8,
|
|
)
|
|
)
|