class TableBorderCharset: def __init__(self, corner_top_left, corner_top_right, corner_bottom_left, corner_bottom_right, line_horizontal, line_vertical, t_junction_horizontal_top, t_junction_horizontal_bottom, t_junction_vertical_left, t_junction_vertical_right, intersection ): self.corner_top_left = corner_top_left self.corner_top_right = corner_top_right self.corner_bottom_left = corner_bottom_left self.corner_bottom_right = corner_bottom_right self.line_horizontal = line_horizontal self.line_vertical = line_vertical self.t_junction_horizontal_top = t_junction_horizontal_top self.t_junction_horizontal_bottom = t_junction_horizontal_bottom self.t_junction_vertical_left = t_junction_vertical_left self.t_junction_vertical_right = t_junction_vertical_right self.intersection = intersection BORDER_THIN = TableBorderCharset( '┌', '┐', '└', '┘', '─', '│', '┴', '┬', '┤', '├', '┼' ) BORDER_THICK = TableBorderCharset( '┏', '┓', '┗', '┛', '━', '┃', '┻', '┳', '┫', '┣', '╋' ) BORDER_DOUBLE = TableBorderCharset( '╔', '╗', '╚', '╝', '═', '║', '╩', '╦', '╣', '╠', '╬' ) BORDER_ROUND = TableBorderCharset( '╭', '╮', '╰', '╯', '─', '│', '┴', '┬', '┤', '├', '┼' ) def get_max_widths(data): max_widths = {} for row in data: for col in row: max_widths[col] = max(len(str(row[col])), max_widths.get(col, 0)) return max_widths def ascii_table(data, *, borders = False, border_charset = BORDER_THIN, column_space = 1, uppercase_column_headers = True): column_widths = get_max_widths(data) columns = list(column_widths.keys()) table = f'''{(column_space * ' ').join([(c.upper() if uppercase_column_headers else c) + ' ' * (column_widths[c] - len(c)) for c in columns])}''' table = '' for col in columns: column_widths[col] = max(column_widths[col], len(col)) for col in columns: table += col.upper() if uppercase_column_headers else col table += ' ' * (column_widths[col] - len(col)) table += ' ' * column_space if borders: table += border_charset.line_vertical + ' ' for row in data: table += '\n' for column in columns: value = str(row.get(column, '')) table += value table += ' ' * (column_widths[column] - len(str(value))) table += column_space * ' ' if borders: table += border_charset.line_vertical + ' ' if borders: table_without_borders = table table_width = max([len(r) for r in table_without_borders.split('\n')]) table = border_charset.corner_top_left for col in columns: table += border_charset.line_horizontal * (column_widths[col] + 2) table += border_charset.t_junction_horizontal_bottom table = table[:-1] + border_charset.corner_top_right table += '\n' for row in table_without_borders.split('\n'): table += border_charset.line_vertical + ' ' table += row table += '\n' table += border_charset.t_junction_vertical_right for i in range(len(columns)): table += border_charset.line_horizontal * (column_widths[columns[i]] + 2) table += border_charset.intersection if i != (len(columns) - 1) else border_charset.t_junction_vertical_left table += '\n' table = '\n'.join(table.split('\n')[:-2]) + '\n' table += border_charset.corner_bottom_left for col in columns: table += border_charset.line_horizontal * (column_widths[col] + 2) table += border_charset.t_junction_horizontal_top table = table[:-1] + border_charset.corner_bottom_right return table