From 0dfe1c9e2780469e3a4696e8fb3e6f717a7ebeb7 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 16 Sep 2022 11:09:35 +0200 Subject: Adding upstream version 3.1.0. Signed-off-by: Daniel Baumann --- terminaltables/base_table.py | 217 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 terminaltables/base_table.py (limited to 'terminaltables/base_table.py') diff --git a/terminaltables/base_table.py b/terminaltables/base_table.py new file mode 100644 index 0000000..281d5a3 --- /dev/null +++ b/terminaltables/base_table.py @@ -0,0 +1,217 @@ +"""Base table class. Define just the bare minimum to build tables.""" + +from terminaltables.build import build_border, build_row, flatten +from terminaltables.width_and_alignment import align_and_pad_cell, max_dimensions + + +class BaseTable(object): + """Base table class. + + :ivar iter table_data: List (empty or list of lists of strings) representing the table. + :ivar str title: Optional title to show within the top border of the table. + :ivar bool inner_column_border: Separates columns. + :ivar bool inner_footing_row_border: Show a border before the last row. + :ivar bool inner_heading_row_border: Show a border after the first row. + :ivar bool inner_row_border: Show a border in between every row. + :ivar bool outer_border: Show the top, left, right, and bottom border. + :ivar dict justify_columns: Horizontal justification. Keys are column indexes (int). Values are right/left/center. + :ivar int padding_left: Number of spaces to pad on the left side of every cell. + :ivar int padding_right: Number of spaces to pad on the right side of every cell. + """ + + CHAR_F_INNER_HORIZONTAL = '-' + CHAR_F_INNER_INTERSECT = '+' + CHAR_F_INNER_VERTICAL = '|' + CHAR_F_OUTER_LEFT_INTERSECT = '+' + CHAR_F_OUTER_LEFT_VERTICAL = '|' + CHAR_F_OUTER_RIGHT_INTERSECT = '+' + CHAR_F_OUTER_RIGHT_VERTICAL = '|' + CHAR_H_INNER_HORIZONTAL = '-' + CHAR_H_INNER_INTERSECT = '+' + CHAR_H_INNER_VERTICAL = '|' + CHAR_H_OUTER_LEFT_INTERSECT = '+' + CHAR_H_OUTER_LEFT_VERTICAL = '|' + CHAR_H_OUTER_RIGHT_INTERSECT = '+' + CHAR_H_OUTER_RIGHT_VERTICAL = '|' + CHAR_INNER_HORIZONTAL = '-' + CHAR_INNER_INTERSECT = '+' + CHAR_INNER_VERTICAL = '|' + CHAR_OUTER_BOTTOM_HORIZONTAL = '-' + CHAR_OUTER_BOTTOM_INTERSECT = '+' + CHAR_OUTER_BOTTOM_LEFT = '+' + CHAR_OUTER_BOTTOM_RIGHT = '+' + CHAR_OUTER_LEFT_INTERSECT = '+' + CHAR_OUTER_LEFT_VERTICAL = '|' + CHAR_OUTER_RIGHT_INTERSECT = '+' + CHAR_OUTER_RIGHT_VERTICAL = '|' + CHAR_OUTER_TOP_HORIZONTAL = '-' + CHAR_OUTER_TOP_INTERSECT = '+' + CHAR_OUTER_TOP_LEFT = '+' + CHAR_OUTER_TOP_RIGHT = '+' + + def __init__(self, table_data, title=None): + """Constructor. + + :param iter table_data: List (empty or list of lists of strings) representing the table. + :param title: Optional title to show within the top border of the table. + """ + self.table_data = table_data + self.title = title + + self.inner_column_border = True + self.inner_footing_row_border = False + self.inner_heading_row_border = True + self.inner_row_border = False + self.outer_border = True + + self.justify_columns = dict() # {0: 'right', 1: 'left', 2: 'center'} + self.padding_left = 1 + self.padding_right = 1 + + def horizontal_border(self, style, outer_widths): + """Build any kind of horizontal border for the table. + + :param str style: Type of border to return. + :param iter outer_widths: List of widths (with padding) for each column. + + :return: Prepared border as a tuple of strings. + :rtype: tuple + """ + if style == 'top': + horizontal = self.CHAR_OUTER_TOP_HORIZONTAL + left = self.CHAR_OUTER_TOP_LEFT + intersect = self.CHAR_OUTER_TOP_INTERSECT if self.inner_column_border else '' + right = self.CHAR_OUTER_TOP_RIGHT + title = self.title + elif style == 'bottom': + horizontal = self.CHAR_OUTER_BOTTOM_HORIZONTAL + left = self.CHAR_OUTER_BOTTOM_LEFT + intersect = self.CHAR_OUTER_BOTTOM_INTERSECT if self.inner_column_border else '' + right = self.CHAR_OUTER_BOTTOM_RIGHT + title = None + elif style == 'heading': + horizontal = self.CHAR_H_INNER_HORIZONTAL + left = self.CHAR_H_OUTER_LEFT_INTERSECT if self.outer_border else '' + intersect = self.CHAR_H_INNER_INTERSECT if self.inner_column_border else '' + right = self.CHAR_H_OUTER_RIGHT_INTERSECT if self.outer_border else '' + title = None + elif style == 'footing': + horizontal = self.CHAR_F_INNER_HORIZONTAL + left = self.CHAR_F_OUTER_LEFT_INTERSECT if self.outer_border else '' + intersect = self.CHAR_F_INNER_INTERSECT if self.inner_column_border else '' + right = self.CHAR_F_OUTER_RIGHT_INTERSECT if self.outer_border else '' + title = None + else: + horizontal = self.CHAR_INNER_HORIZONTAL + left = self.CHAR_OUTER_LEFT_INTERSECT if self.outer_border else '' + intersect = self.CHAR_INNER_INTERSECT if self.inner_column_border else '' + right = self.CHAR_OUTER_RIGHT_INTERSECT if self.outer_border else '' + title = None + return build_border(outer_widths, horizontal, left, intersect, right, title) + + def gen_row_lines(self, row, style, inner_widths, height): + r"""Combine cells in row and group them into lines with vertical borders. + + Caller is expected to pass yielded lines to ''.join() to combine them into a printable line. Caller must append + newline character to the end of joined line. + + In: + ['Row One Column One', 'Two', 'Three'] + Out: + [ + ('|', ' Row One Column One ', '|', ' Two ', '|', ' Three ', '|'), + ] + + In: + ['Row One\nColumn One', 'Two', 'Three'], + Out: + [ + ('|', ' Row One ', '|', ' Two ', '|', ' Three ', '|'), + ('|', ' Column One ', '|', ' ', '|', ' ', '|'), + ] + + :param iter row: One row in the table. List of cells. + :param str style: Type of border characters to use. + :param iter inner_widths: List of widths (no padding) for each column. + :param int height: Inner height (no padding) (number of lines) to expand row to. + + :return: Yields lines split into components in a list. Caller must ''.join() line. + """ + cells_in_row = list() + + # Resize row if it doesn't have enough cells. + if len(row) != len(inner_widths): + row = row + [''] * (len(inner_widths) - len(row)) + + # Pad and align each cell. Split each cell into lines to support multi-line cells. + for i, cell in enumerate(row): + align = (self.justify_columns.get(i),) + inner_dimensions = (inner_widths[i], height) + padding = (self.padding_left, self.padding_right, 0, 0) + cells_in_row.append(align_and_pad_cell(cell, align, inner_dimensions, padding)) + + # Determine border characters. + if style == 'heading': + left = self.CHAR_H_OUTER_LEFT_VERTICAL if self.outer_border else '' + center = self.CHAR_H_INNER_VERTICAL if self.inner_column_border else '' + right = self.CHAR_H_OUTER_RIGHT_VERTICAL if self.outer_border else '' + elif style == 'footing': + left = self.CHAR_F_OUTER_LEFT_VERTICAL if self.outer_border else '' + center = self.CHAR_F_INNER_VERTICAL if self.inner_column_border else '' + right = self.CHAR_F_OUTER_RIGHT_VERTICAL if self.outer_border else '' + else: + left = self.CHAR_OUTER_LEFT_VERTICAL if self.outer_border else '' + center = self.CHAR_INNER_VERTICAL if self.inner_column_border else '' + right = self.CHAR_OUTER_RIGHT_VERTICAL if self.outer_border else '' + + # Yield each line. + for line in build_row(cells_in_row, left, center, right): + yield line + + def gen_table(self, inner_widths, inner_heights, outer_widths): + """Combine everything and yield every line of the entire table with borders. + + :param iter inner_widths: List of widths (no padding) for each column. + :param iter inner_heights: List of heights (no padding) for each row. + :param iter outer_widths: List of widths (with padding) for each column. + :return: + """ + # Yield top border. + if self.outer_border: + yield self.horizontal_border('top', outer_widths) + + # Yield table body. + row_count = len(self.table_data) + last_row_index, before_last_row_index = row_count - 1, row_count - 2 + for i, row in enumerate(self.table_data): + # Yield the row line by line (e.g. multi-line rows). + if self.inner_heading_row_border and i == 0: + style = 'heading' + elif self.inner_footing_row_border and i == last_row_index: + style = 'footing' + else: + style = 'row' + for line in self.gen_row_lines(row, style, inner_widths, inner_heights[i]): + yield line + # If this is the last row then break. No separator needed. + if i == last_row_index: + break + # Yield heading separator. + if self.inner_heading_row_border and i == 0: + yield self.horizontal_border('heading', outer_widths) + # Yield footing separator. + elif self.inner_footing_row_border and i == before_last_row_index: + yield self.horizontal_border('footing', outer_widths) + # Yield row separator. + elif self.inner_row_border: + yield self.horizontal_border('row', outer_widths) + + # Yield bottom border. + if self.outer_border: + yield self.horizontal_border('bottom', outer_widths) + + @property + def table(self): + """Return a large string of the entire table ready to be printed to the terminal.""" + dimensions = max_dimensions(self.table_data, self.padding_left, self.padding_right)[:3] + return flatten(self.gen_table(*dimensions)) -- cgit v1.2.3