# -*- 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, ) )