114 lines
2.5 KiB
Python
114 lines
2.5 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,
|
||
|
):
|
||
|
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)
|
||
|
)
|
||
|
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,
|
||
|
)
|
||
|
)
|