Skip to content

cmd2.table_creator

cmd2.table_creator

cmd2 table creation API This API is built upon two core classes: Column and TableCreator The general use case is to inherit from TableCreator to create a table class with custom formatting options. There are already implemented and ready-to-use examples of this below TableCreator's code.

EMPTY module-attribute

EMPTY = ''

SPACE module-attribute

SPACE = ' '

HorizontalAlignment

Bases: Enum

Horizontal alignment of text in a cell

LEFT class-attribute instance-attribute

LEFT = 1

CENTER class-attribute instance-attribute

CENTER = 2

RIGHT class-attribute instance-attribute

RIGHT = 3

VerticalAlignment

Bases: Enum

Vertical alignment of text in a cell

TOP class-attribute instance-attribute

TOP = 1

MIDDLE class-attribute instance-attribute

MIDDLE = 2

BOTTOM class-attribute instance-attribute

BOTTOM = 3

Column

Column(header, *, width=None, header_horiz_align=LEFT, header_vert_align=BOTTOM, style_header_text=True, data_horiz_align=LEFT, data_vert_align=TOP, style_data_text=True, max_data_lines=INFINITY)

Table column configuration

Column initializer

PARAMETER DESCRIPTION
header

label for column header

TYPE: str

width

display width of column. This does not account for any borders or padding which may be added (e.g pre_line, inter_cell, and post_line). Header and data text wrap within this width using word-based wrapping (defaults to actual width of header or 1 if header is blank)

TYPE: Optional[int] DEFAULT: None

header_horiz_align

horizontal alignment of header cells (defaults to left)

TYPE: HorizontalAlignment DEFAULT: LEFT

header_vert_align

vertical alignment of header cells (defaults to bottom)

TYPE: VerticalAlignment DEFAULT: BOTTOM

style_header_text

if True, then the table is allowed to apply styles to the header text, which may conflict with any styles the header already has. If False, the header is printed as is. Table classes which apply style to headers must account for the value of this flag. (defaults to True)

TYPE: bool DEFAULT: True

data_horiz_align

horizontal alignment of data cells (defaults to left)

TYPE: HorizontalAlignment DEFAULT: LEFT

data_vert_align

vertical alignment of data cells (defaults to top)

TYPE: VerticalAlignment DEFAULT: TOP

style_data_text

if True, then the table is allowed to apply styles to the data text, which may conflict with any styles the data already has. If False, the data is printed as is. Table classes which apply style to data must account for the value of this flag. (defaults to True)

TYPE: bool DEFAULT: True

max_data_lines

maximum lines allowed in a data cell. If line count exceeds this, then the final line displayed will be truncated with an ellipsis. (defaults to INFINITY)

TYPE: Union[int, float] DEFAULT: INFINITY

RAISES DESCRIPTION
ValueError

if width is less than 1

ValueError

if max_data_lines is less than 1

Source code in cmd2/table_creator.py
def __init__(
    self,
    header: str,
    *,
    width: Optional[int] = None,
    header_horiz_align: HorizontalAlignment = HorizontalAlignment.LEFT,
    header_vert_align: VerticalAlignment = VerticalAlignment.BOTTOM,
    style_header_text: bool = True,
    data_horiz_align: HorizontalAlignment = HorizontalAlignment.LEFT,
    data_vert_align: VerticalAlignment = VerticalAlignment.TOP,
    style_data_text: bool = True,
    max_data_lines: Union[int, float] = constants.INFINITY,
) -> None:
    """
    Column initializer

    :param header: label for column header
    :param width: display width of column. This does not account for any borders or padding which
                  may be added (e.g pre_line, inter_cell, and post_line). Header and data text wrap within
                  this width using word-based wrapping (defaults to actual width of header or 1 if header is blank)
    :param header_horiz_align: horizontal alignment of header cells (defaults to left)
    :param header_vert_align: vertical alignment of header cells (defaults to bottom)
    :param style_header_text: if True, then the table is allowed to apply styles to the header text, which may
                              conflict with any styles the header already has. If False, the header is printed as is.
                              Table classes which apply style to headers must account for the value of this flag.
                              (defaults to True)
    :param data_horiz_align: horizontal alignment of data cells (defaults to left)
    :param data_vert_align: vertical alignment of data cells (defaults to top)
    :param style_data_text: if True, then the table is allowed to apply styles to the data text, which may
                            conflict with any styles the data already has. If False, the data is printed as is.
                            Table classes which apply style to data must account for the value of this flag.
                            (defaults to True)
    :param max_data_lines: maximum lines allowed in a data cell. If line count exceeds this, then the final
                           line displayed will be truncated with an ellipsis. (defaults to INFINITY)
    :raises ValueError: if width is less than 1
    :raises ValueError: if max_data_lines is less than 1
    """
    self.header = header

    if width is not None and width < 1:
        raise ValueError("Column width cannot be less than 1")
    else:
        self.width: int = width if width is not None else -1

    self.header_horiz_align = header_horiz_align
    self.header_vert_align = header_vert_align
    self.style_header_text = style_header_text

    self.data_horiz_align = data_horiz_align
    self.data_vert_align = data_vert_align
    self.style_data_text = style_data_text

    if max_data_lines < 1:
        raise ValueError("Max data lines cannot be less than 1")

    self.max_data_lines = max_data_lines

header instance-attribute

header = header

width instance-attribute

width = width if width is not None else -1

header_horiz_align instance-attribute

header_horiz_align = header_horiz_align

header_vert_align instance-attribute

header_vert_align = header_vert_align

style_header_text instance-attribute

style_header_text = style_header_text

data_horiz_align instance-attribute

data_horiz_align = data_horiz_align

data_vert_align instance-attribute

data_vert_align = data_vert_align

style_data_text instance-attribute

style_data_text = style_data_text

max_data_lines instance-attribute

max_data_lines = max_data_lines

TableCreator

TableCreator(cols, *, tab_width=4)

Base table creation class. This class handles ANSI style sequences and characters with display widths greater than 1 when performing width calculations. It was designed with the ability to build tables one row at a time. This helps when you have large data sets that you don't want to hold in memory or when you receive portions of the data set incrementally.

TableCreator has one public method: generate_row()

This function and the Column class provide all features needed to build tables with headers, borders, colors, horizontal and vertical alignment, and wrapped text. However, it's generally easier to inherit from this class and implement a more granular API rather than use TableCreator directly. There are ready-to-use examples of this defined after this class.

TableCreator initializer

PARAMETER DESCRIPTION
cols

column definitions for this table

TYPE: Sequence[Column]

tab_width

all tabs will be replaced with this many spaces. If a row's fill_char is a tab, then it will be converted to one space.

TYPE: int DEFAULT: 4

RAISES DESCRIPTION
ValueError

if tab_width is less than 1

Source code in cmd2/table_creator.py
def __init__(self, cols: Sequence[Column], *, tab_width: int = 4) -> None:
    """
    TableCreator initializer

    :param cols: column definitions for this table
    :param tab_width: all tabs will be replaced with this many spaces. If a row's fill_char is a tab,
                      then it will be converted to one space.
    :raises ValueError: if tab_width is less than 1
    """
    if tab_width < 1:
        raise ValueError("Tab width cannot be less than 1")

    self.cols = copy.copy(cols)
    self.tab_width = tab_width

    for col in self.cols:
        # Replace tabs before calculating width of header strings
        col.header = col.header.replace('\t', SPACE * self.tab_width)

        # For headers with the width not yet set, use the width of the
        # widest line in the header or 1 if the header has no width
        if col.width <= 0:
            col.width = max(1, ansi.widest_line(col.header))

cols instance-attribute

cols = copy(cols)

tab_width instance-attribute

tab_width = tab_width

generate_row

generate_row(row_data, is_header, *, fill_char=SPACE, pre_line=EMPTY, inter_cell=2 * SPACE, post_line=EMPTY)

Generate a header or data table row

PARAMETER DESCRIPTION
row_data

data with an entry for each column in the row

TYPE: Sequence[Any]

is_header

True if writing a header cell, otherwise writing a data cell. This determines whether to use header or data alignment settings as well as maximum lines to wrap.

TYPE: bool

fill_char

character that fills remaining space in a cell. Defaults to space. If this is a tab, then it will be converted to one space. (Cannot be a line breaking character)

TYPE: str DEFAULT: SPACE

pre_line

string to print before each line of a row. This can be used for a left row border and padding before the first cell's text. (Defaults to blank)

TYPE: str DEFAULT: EMPTY

inter_cell

string to print where two cells meet. This can be used for a border between cells and padding between it and the 2 cells' text. (Defaults to 2 spaces)

TYPE: str DEFAULT: 2 * SPACE

post_line

string to print after each line of a row. This can be used for padding after the last cell's text and a right row border. (Defaults to blank)

TYPE: str DEFAULT: EMPTY

RETURNS DESCRIPTION
str

row string

RAISES DESCRIPTION
ValueError

if row_data isn't the same length as self.cols

TypeError

if fill_char is more than one character (not including ANSI style sequences)

ValueError

if fill_char, pre_line, inter_cell, or post_line contains an unprintable character like a newline

Source code in cmd2/table_creator.py
def generate_row(
    self,
    row_data: Sequence[Any],
    is_header: bool,
    *,
    fill_char: str = SPACE,
    pre_line: str = EMPTY,
    inter_cell: str = (2 * SPACE),
    post_line: str = EMPTY,
) -> str:
    """
    Generate a header or data table row

    :param row_data: data with an entry for each column in the row
    :param is_header: True if writing a header cell, otherwise writing a data cell. This determines whether to
                      use header or data alignment settings as well as maximum lines to wrap.
    :param fill_char: character that fills remaining space in a cell. Defaults to space. If this is a tab,
                      then it will be converted to one space. (Cannot be a line breaking character)
    :param pre_line: string to print before each line of a row. This can be used for a left row border and
                     padding before the first cell's text. (Defaults to blank)
    :param inter_cell: string to print where two cells meet. This can be used for a border between cells and padding
                       between it and the 2 cells' text. (Defaults to 2 spaces)
    :param post_line: string to print after each line of a row. This can be used for padding after
                      the last cell's text and a right row border. (Defaults to blank)
    :return: row string
    :raises ValueError: if row_data isn't the same length as self.cols
    :raises TypeError: if fill_char is more than one character (not including ANSI style sequences)
    :raises ValueError: if fill_char, pre_line, inter_cell, or post_line contains an unprintable
             character like a newline
    """

    class Cell:
        """Inner class which represents a table cell"""

        def __init__(self) -> None:
            # Data in this cell split into individual lines
            self.lines: Deque[str] = deque()

            # Display width of this cell
            self.width = 0

    if len(row_data) != len(self.cols):
        raise ValueError("Length of row_data must match length of cols")

    # Replace tabs (tabs in data strings will be handled in _generate_cell_lines())
    fill_char = fill_char.replace('\t', SPACE)
    pre_line = pre_line.replace('\t', SPACE * self.tab_width)
    inter_cell = inter_cell.replace('\t', SPACE * self.tab_width)
    post_line = post_line.replace('\t', SPACE * self.tab_width)

    # Validate fill_char character count
    if len(ansi.strip_style(fill_char)) != 1:
        raise TypeError("Fill character must be exactly one character long")

    # Look for unprintable characters
    validation_dict = {'fill_char': fill_char, 'pre_line': pre_line, 'inter_cell': inter_cell, 'post_line': post_line}
    for key, val in validation_dict.items():
        if ansi.style_aware_wcswidth(val) == -1:
            raise ValueError(f"{key} contains an unprintable character")

    # Number of lines this row uses
    total_lines = 0

    # Generate the cells for this row
    cells = list()

    for col_index, col in enumerate(self.cols):
        cell = Cell()
        cell.lines, cell.width = self._generate_cell_lines(row_data[col_index], is_header, col, fill_char)
        cells.append(cell)
        total_lines = max(len(cell.lines), total_lines)

    row_buf = io.StringIO()

    # Vertically align each cell
    for cell_index, cell in enumerate(cells):
        col = self.cols[cell_index]
        vert_align = col.header_vert_align if is_header else col.data_vert_align

        # Check if this cell need vertical filler
        line_diff = total_lines - len(cell.lines)
        if line_diff == 0:
            continue

        # Add vertical filler lines
        padding_line = utils.align_left(EMPTY, fill_char=fill_char, width=cell.width)
        if vert_align == VerticalAlignment.TOP:
            to_top = 0
            to_bottom = line_diff
        elif vert_align == VerticalAlignment.MIDDLE:
            to_top = line_diff // 2
            to_bottom = line_diff - to_top
        else:
            to_top = line_diff
            to_bottom = 0

        for i in range(to_top):
            cell.lines.appendleft(padding_line)
        for i in range(to_bottom):
            cell.lines.append(padding_line)

    # Build this row one line at a time
    for line_index in range(total_lines):
        for cell_index, cell in enumerate(cells):
            if cell_index == 0:
                row_buf.write(pre_line)

            row_buf.write(cell.lines[line_index])

            if cell_index < len(self.cols) - 1:
                row_buf.write(inter_cell)
            if cell_index == len(self.cols) - 1:
                row_buf.write(post_line)

        # Add a newline if this is not the last line
        if line_index < total_lines - 1:
            row_buf.write('\n')

    return row_buf.getvalue()

SimpleTable

SimpleTable(cols, *, column_spacing=2, tab_width=4, divider_char='-', header_bg=None, data_bg=None)

Bases: TableCreator

Implementation of TableCreator which generates a borderless table with an optional divider row after the header. This class can be used to create the whole table at once or one row at a time.

SimpleTable initializer

PARAMETER DESCRIPTION
cols

column definitions for this table

TYPE: Sequence[Column]

column_spacing

how many spaces to place between columns. Defaults to 2.

TYPE: int DEFAULT: 2

tab_width

all tabs will be replaced with this many spaces. If a row's fill_char is a tab, then it will be converted to one space.

TYPE: int DEFAULT: 4

divider_char

optional character used to build the header divider row. Set this to blank or None if you don't want a divider row. Defaults to dash. (Cannot be a line breaking character)

TYPE: Optional[str] DEFAULT: '-'

header_bg

optional background color for header cells (defaults to None)

TYPE: Optional[BgColor] DEFAULT: None

data_bg

optional background color for data cells (defaults to None)

TYPE: Optional[BgColor] DEFAULT: None

RAISES DESCRIPTION
ValueError

if tab_width is less than 1

ValueError

if column_spacing is less than 0

TypeError

if divider_char is longer than one character

ValueError

if divider_char is an unprintable character

Source code in cmd2/table_creator.py
def __init__(
    self,
    cols: Sequence[Column],
    *,
    column_spacing: int = 2,
    tab_width: int = 4,
    divider_char: Optional[str] = '-',
    header_bg: Optional[ansi.BgColor] = None,
    data_bg: Optional[ansi.BgColor] = None,
) -> None:
    """
    SimpleTable initializer

    :param cols: column definitions for this table
    :param column_spacing: how many spaces to place between columns. Defaults to 2.
    :param tab_width: all tabs will be replaced with this many spaces. If a row's fill_char is a tab,
                      then it will be converted to one space.
    :param divider_char: optional character used to build the header divider row. Set this to blank or None if you don't
                         want a divider row. Defaults to dash. (Cannot be a line breaking character)
    :param header_bg: optional background color for header cells (defaults to None)
    :param data_bg: optional background color for data cells (defaults to None)
    :raises ValueError: if tab_width is less than 1
    :raises ValueError: if column_spacing is less than 0
    :raises TypeError: if divider_char is longer than one character
    :raises ValueError: if divider_char is an unprintable character
    """
    super().__init__(cols, tab_width=tab_width)

    if column_spacing < 0:
        raise ValueError("Column spacing cannot be less than 0")

    self.column_spacing = column_spacing

    if divider_char == '':
        divider_char = None

    if divider_char is not None:
        if len(ansi.strip_style(divider_char)) != 1:
            raise TypeError("Divider character must be exactly one character long")

        divider_char_width = ansi.style_aware_wcswidth(divider_char)
        if divider_char_width == -1:
            raise ValueError("Divider character is an unprintable character")

    self.divider_char = divider_char
    self.header_bg = header_bg
    self.data_bg = data_bg

cols instance-attribute

cols = copy(cols)

tab_width instance-attribute

tab_width = tab_width

column_spacing instance-attribute

column_spacing = column_spacing

divider_char instance-attribute

divider_char = divider_char

header_bg instance-attribute

header_bg = header_bg

data_bg instance-attribute

data_bg = data_bg

generate_row

generate_row(row_data, is_header, *, fill_char=SPACE, pre_line=EMPTY, inter_cell=2 * SPACE, post_line=EMPTY)

Generate a header or data table row

PARAMETER DESCRIPTION
row_data

data with an entry for each column in the row

TYPE: Sequence[Any]

is_header

True if writing a header cell, otherwise writing a data cell. This determines whether to use header or data alignment settings as well as maximum lines to wrap.

TYPE: bool

fill_char

character that fills remaining space in a cell. Defaults to space. If this is a tab, then it will be converted to one space. (Cannot be a line breaking character)

TYPE: str DEFAULT: SPACE

pre_line

string to print before each line of a row. This can be used for a left row border and padding before the first cell's text. (Defaults to blank)

TYPE: str DEFAULT: EMPTY

inter_cell

string to print where two cells meet. This can be used for a border between cells and padding between it and the 2 cells' text. (Defaults to 2 spaces)

TYPE: str DEFAULT: 2 * SPACE

post_line

string to print after each line of a row. This can be used for padding after the last cell's text and a right row border. (Defaults to blank)

TYPE: str DEFAULT: EMPTY

RETURNS DESCRIPTION
str

row string

RAISES DESCRIPTION
ValueError

if row_data isn't the same length as self.cols

TypeError

if fill_char is more than one character (not including ANSI style sequences)

ValueError

if fill_char, pre_line, inter_cell, or post_line contains an unprintable character like a newline

Source code in cmd2/table_creator.py
def generate_row(
    self,
    row_data: Sequence[Any],
    is_header: bool,
    *,
    fill_char: str = SPACE,
    pre_line: str = EMPTY,
    inter_cell: str = (2 * SPACE),
    post_line: str = EMPTY,
) -> str:
    """
    Generate a header or data table row

    :param row_data: data with an entry for each column in the row
    :param is_header: True if writing a header cell, otherwise writing a data cell. This determines whether to
                      use header or data alignment settings as well as maximum lines to wrap.
    :param fill_char: character that fills remaining space in a cell. Defaults to space. If this is a tab,
                      then it will be converted to one space. (Cannot be a line breaking character)
    :param pre_line: string to print before each line of a row. This can be used for a left row border and
                     padding before the first cell's text. (Defaults to blank)
    :param inter_cell: string to print where two cells meet. This can be used for a border between cells and padding
                       between it and the 2 cells' text. (Defaults to 2 spaces)
    :param post_line: string to print after each line of a row. This can be used for padding after
                      the last cell's text and a right row border. (Defaults to blank)
    :return: row string
    :raises ValueError: if row_data isn't the same length as self.cols
    :raises TypeError: if fill_char is more than one character (not including ANSI style sequences)
    :raises ValueError: if fill_char, pre_line, inter_cell, or post_line contains an unprintable
             character like a newline
    """

    class Cell:
        """Inner class which represents a table cell"""

        def __init__(self) -> None:
            # Data in this cell split into individual lines
            self.lines: Deque[str] = deque()

            # Display width of this cell
            self.width = 0

    if len(row_data) != len(self.cols):
        raise ValueError("Length of row_data must match length of cols")

    # Replace tabs (tabs in data strings will be handled in _generate_cell_lines())
    fill_char = fill_char.replace('\t', SPACE)
    pre_line = pre_line.replace('\t', SPACE * self.tab_width)
    inter_cell = inter_cell.replace('\t', SPACE * self.tab_width)
    post_line = post_line.replace('\t', SPACE * self.tab_width)

    # Validate fill_char character count
    if len(ansi.strip_style(fill_char)) != 1:
        raise TypeError("Fill character must be exactly one character long")

    # Look for unprintable characters
    validation_dict = {'fill_char': fill_char, 'pre_line': pre_line, 'inter_cell': inter_cell, 'post_line': post_line}
    for key, val in validation_dict.items():
        if ansi.style_aware_wcswidth(val) == -1:
            raise ValueError(f"{key} contains an unprintable character")

    # Number of lines this row uses
    total_lines = 0

    # Generate the cells for this row
    cells = list()

    for col_index, col in enumerate(self.cols):
        cell = Cell()
        cell.lines, cell.width = self._generate_cell_lines(row_data[col_index], is_header, col, fill_char)
        cells.append(cell)
        total_lines = max(len(cell.lines), total_lines)

    row_buf = io.StringIO()

    # Vertically align each cell
    for cell_index, cell in enumerate(cells):
        col = self.cols[cell_index]
        vert_align = col.header_vert_align if is_header else col.data_vert_align

        # Check if this cell need vertical filler
        line_diff = total_lines - len(cell.lines)
        if line_diff == 0:
            continue

        # Add vertical filler lines
        padding_line = utils.align_left(EMPTY, fill_char=fill_char, width=cell.width)
        if vert_align == VerticalAlignment.TOP:
            to_top = 0
            to_bottom = line_diff
        elif vert_align == VerticalAlignment.MIDDLE:
            to_top = line_diff // 2
            to_bottom = line_diff - to_top
        else:
            to_top = line_diff
            to_bottom = 0

        for i in range(to_top):
            cell.lines.appendleft(padding_line)
        for i in range(to_bottom):
            cell.lines.append(padding_line)

    # Build this row one line at a time
    for line_index in range(total_lines):
        for cell_index, cell in enumerate(cells):
            if cell_index == 0:
                row_buf.write(pre_line)

            row_buf.write(cell.lines[line_index])

            if cell_index < len(self.cols) - 1:
                row_buf.write(inter_cell)
            if cell_index == len(self.cols) - 1:
                row_buf.write(post_line)

        # Add a newline if this is not the last line
        if line_index < total_lines - 1:
            row_buf.write('\n')

    return row_buf.getvalue()

apply_header_bg

apply_header_bg(value)

If defined, apply the header background color to header text

PARAMETER DESCRIPTION
value

object whose text is to be colored

TYPE: Any

RETURNS DESCRIPTION
str

formatted text

Source code in cmd2/table_creator.py
def apply_header_bg(self, value: Any) -> str:
    """
    If defined, apply the header background color to header text
    :param value: object whose text is to be colored
    :return: formatted text
    """
    if self.header_bg is None:
        return str(value)
    return ansi.style(value, bg=self.header_bg)

apply_data_bg

apply_data_bg(value)

If defined, apply the data background color to data text

PARAMETER DESCRIPTION
value

object whose text is to be colored

TYPE: Any

RETURNS DESCRIPTION
str

formatted data string

Source code in cmd2/table_creator.py
def apply_data_bg(self, value: Any) -> str:
    """
    If defined, apply the data background color to data text
    :param value: object whose text is to be colored
    :return: formatted data string
    """
    if self.data_bg is None:
        return str(value)
    return ansi.style(value, bg=self.data_bg)

base_width classmethod

base_width(num_cols, *, column_spacing=2)

Utility method to calculate the display width required for a table before data is added to it. This is useful when determining how wide to make your columns to have a table be a specific width.

PARAMETER DESCRIPTION
num_cols

how many columns the table will have

TYPE: int

column_spacing

how many spaces to place between columns. Defaults to 2.

TYPE: int DEFAULT: 2

RETURNS DESCRIPTION
int

base width

RAISES DESCRIPTION
ValueError

if column_spacing is less than 0

ValueError

if num_cols is less than 1

Source code in cmd2/table_creator.py
@classmethod
def base_width(cls, num_cols: int, *, column_spacing: int = 2) -> int:
    """
    Utility method to calculate the display width required for a table before data is added to it.
    This is useful when determining how wide to make your columns to have a table be a specific width.

    :param num_cols: how many columns the table will have
    :param column_spacing: how many spaces to place between columns. Defaults to 2.
    :return: base width
    :raises ValueError: if column_spacing is less than 0
    :raises ValueError: if num_cols is less than 1
    """
    if num_cols < 1:
        raise ValueError("Column count cannot be less than 1")

    data_str = SPACE
    data_width = ansi.style_aware_wcswidth(data_str) * num_cols

    tbl = cls([Column(data_str)] * num_cols, column_spacing=column_spacing)
    data_row = tbl.generate_data_row([data_str] * num_cols)

    return ansi.style_aware_wcswidth(data_row) - data_width

total_width

total_width()

Calculate the total display width of this table

Source code in cmd2/table_creator.py
def total_width(self) -> int:
    """Calculate the total display width of this table"""
    base_width = self.base_width(len(self.cols), column_spacing=self.column_spacing)
    data_width = sum(col.width for col in self.cols)
    return base_width + data_width

generate_header

generate_header()

Generate table header with an optional divider row

Source code in cmd2/table_creator.py
def generate_header(self) -> str:
    """Generate table header with an optional divider row"""
    header_buf = io.StringIO()

    fill_char = self.apply_header_bg(SPACE)
    inter_cell = self.apply_header_bg(self.column_spacing * SPACE)

    # Apply background color to header text in Columns which allow it
    to_display: List[Any] = []
    for col in self.cols:
        if col.style_header_text:
            to_display.append(self.apply_header_bg(col.header))
        else:
            to_display.append(col.header)

    # Create the header labels
    header_labels = self.generate_row(to_display, is_header=True, fill_char=fill_char, inter_cell=inter_cell)
    header_buf.write(header_labels)

    # Add the divider if necessary
    divider = self.generate_divider()
    if divider:
        header_buf.write('\n' + divider)

    return header_buf.getvalue()

generate_divider

generate_divider()

Generate divider row

Source code in cmd2/table_creator.py
def generate_divider(self) -> str:
    """Generate divider row"""
    if self.divider_char is None:
        return ''

    return utils.align_left('', fill_char=self.divider_char, width=self.total_width())

generate_data_row

generate_data_row(row_data)

Generate a data row

PARAMETER DESCRIPTION
row_data

data with an entry for each column in the row

TYPE: Sequence[Any]

RETURNS DESCRIPTION
str

data row string

RAISES DESCRIPTION
ValueError

if row_data isn't the same length as self.cols

Source code in cmd2/table_creator.py
def generate_data_row(self, row_data: Sequence[Any]) -> str:
    """
    Generate a data row

    :param row_data: data with an entry for each column in the row
    :return: data row string
    :raises ValueError: if row_data isn't the same length as self.cols
    """
    if len(row_data) != len(self.cols):
        raise ValueError("Length of row_data must match length of cols")

    fill_char = self.apply_data_bg(SPACE)
    inter_cell = self.apply_data_bg(self.column_spacing * SPACE)

    # Apply background color to data text in Columns which allow it
    to_display: List[Any] = []
    for index, col in enumerate(self.cols):
        if col.style_data_text:
            to_display.append(self.apply_data_bg(row_data[index]))
        else:
            to_display.append(row_data[index])

    return self.generate_row(to_display, is_header=False, fill_char=fill_char, inter_cell=inter_cell)

generate_table

generate_table(table_data, *, include_header=True, row_spacing=1)

Generate a table from a data set

PARAMETER DESCRIPTION
table_data

Data with an entry for each data row of the table. Each entry should have data for each column in the row.

TYPE: Sequence[Sequence[Any]]

include_header

If True, then a header will be included at top of table. (Defaults to True)

TYPE: bool DEFAULT: True

row_spacing

A number 0 or greater specifying how many blank lines to place between each row (Defaults to 1)

TYPE: int DEFAULT: 1

RAISES DESCRIPTION
ValueError

if row_spacing is less than 0

Source code in cmd2/table_creator.py
def generate_table(self, table_data: Sequence[Sequence[Any]], *, include_header: bool = True, row_spacing: int = 1) -> str:
    """
    Generate a table from a data set

    :param table_data: Data with an entry for each data row of the table. Each entry should have data for
                       each column in the row.
    :param include_header: If True, then a header will be included at top of table. (Defaults to True)
    :param row_spacing: A number 0 or greater specifying how many blank lines to place between
                        each row (Defaults to 1)
    :raises ValueError: if row_spacing is less than 0
    """
    if row_spacing < 0:
        raise ValueError("Row spacing cannot be less than 0")

    table_buf = io.StringIO()

    if include_header:
        header = self.generate_header()
        table_buf.write(header)
        if len(table_data) > 0:
            table_buf.write('\n')

    row_divider = utils.align_left('', fill_char=self.apply_data_bg(SPACE), width=self.total_width()) + '\n'

    for index, row_data in enumerate(table_data):
        if index > 0 and row_spacing > 0:
            table_buf.write(row_spacing * row_divider)

        row = self.generate_data_row(row_data)
        table_buf.write(row)
        if index < len(table_data) - 1:
            table_buf.write('\n')

    return table_buf.getvalue()

BorderedTable

BorderedTable(cols, *, tab_width=4, column_borders=True, padding=1, border_fg=None, border_bg=None, header_bg=None, data_bg=None)

Bases: TableCreator

Implementation of TableCreator which generates a table with borders around the table and between rows. Borders between columns can also be toggled. This class can be used to create the whole table at once or one row at a time.

BorderedTable initializer

PARAMETER DESCRIPTION
cols

column definitions for this table

TYPE: Sequence[Column]

tab_width

all tabs will be replaced with this many spaces. If a row's fill_char is a tab, then it will be converted to one space.

TYPE: int DEFAULT: 4

column_borders

if True, borders between columns will be included. This gives the table a grid-like appearance. Turning off column borders results in a unified appearance between a row's cells. (Defaults to True)

TYPE: bool DEFAULT: True

padding

number of spaces between text and left/right borders of cell

TYPE: int DEFAULT: 1

border_fg

optional foreground color for borders (defaults to None)

TYPE: Optional[FgColor] DEFAULT: None

border_bg

optional background color for borders (defaults to None)

TYPE: Optional[BgColor] DEFAULT: None

header_bg

optional background color for header cells (defaults to None)

TYPE: Optional[BgColor] DEFAULT: None

data_bg

optional background color for data cells (defaults to None)

TYPE: Optional[BgColor] DEFAULT: None

RAISES DESCRIPTION
ValueError

if tab_width is less than 1

ValueError

if padding is less than 0

Source code in cmd2/table_creator.py
def __init__(
    self,
    cols: Sequence[Column],
    *,
    tab_width: int = 4,
    column_borders: bool = True,
    padding: int = 1,
    border_fg: Optional[ansi.FgColor] = None,
    border_bg: Optional[ansi.BgColor] = None,
    header_bg: Optional[ansi.BgColor] = None,
    data_bg: Optional[ansi.BgColor] = None,
) -> None:
    """
    BorderedTable initializer

    :param cols: column definitions for this table
    :param tab_width: all tabs will be replaced with this many spaces. If a row's fill_char is a tab,
                      then it will be converted to one space.
    :param column_borders: if True, borders between columns will be included. This gives the table a grid-like
                           appearance. Turning off column borders results in a unified appearance between
                           a row's cells. (Defaults to True)
    :param padding: number of spaces between text and left/right borders of cell
    :param border_fg: optional foreground color for borders (defaults to None)
    :param border_bg: optional background color for borders (defaults to None)
    :param header_bg: optional background color for header cells (defaults to None)
    :param data_bg: optional background color for data cells (defaults to None)
    :raises ValueError: if tab_width is less than 1
    :raises ValueError: if padding is less than 0
    """
    super().__init__(cols, tab_width=tab_width)
    self.empty_data = [EMPTY] * len(self.cols)
    self.column_borders = column_borders

    if padding < 0:
        raise ValueError("Padding cannot be less than 0")
    self.padding = padding

    self.border_fg = border_fg
    self.border_bg = border_bg
    self.header_bg = header_bg
    self.data_bg = data_bg

cols instance-attribute

cols = copy(cols)

tab_width instance-attribute

tab_width = tab_width

empty_data instance-attribute

empty_data = [EMPTY] * len(cols)

column_borders instance-attribute

column_borders = column_borders

padding instance-attribute

padding = padding

border_fg instance-attribute

border_fg = border_fg

border_bg instance-attribute

border_bg = border_bg

header_bg instance-attribute

header_bg = header_bg

data_bg instance-attribute

data_bg = data_bg

generate_row

generate_row(row_data, is_header, *, fill_char=SPACE, pre_line=EMPTY, inter_cell=2 * SPACE, post_line=EMPTY)

Generate a header or data table row

PARAMETER DESCRIPTION
row_data

data with an entry for each column in the row

TYPE: Sequence[Any]

is_header

True if writing a header cell, otherwise writing a data cell. This determines whether to use header or data alignment settings as well as maximum lines to wrap.

TYPE: bool

fill_char

character that fills remaining space in a cell. Defaults to space. If this is a tab, then it will be converted to one space. (Cannot be a line breaking character)

TYPE: str DEFAULT: SPACE

pre_line

string to print before each line of a row. This can be used for a left row border and padding before the first cell's text. (Defaults to blank)

TYPE: str DEFAULT: EMPTY

inter_cell

string to print where two cells meet. This can be used for a border between cells and padding between it and the 2 cells' text. (Defaults to 2 spaces)

TYPE: str DEFAULT: 2 * SPACE

post_line

string to print after each line of a row. This can be used for padding after the last cell's text and a right row border. (Defaults to blank)

TYPE: str DEFAULT: EMPTY

RETURNS DESCRIPTION
str

row string

RAISES DESCRIPTION
ValueError

if row_data isn't the same length as self.cols

TypeError

if fill_char is more than one character (not including ANSI style sequences)

ValueError

if fill_char, pre_line, inter_cell, or post_line contains an unprintable character like a newline

Source code in cmd2/table_creator.py
def generate_row(
    self,
    row_data: Sequence[Any],
    is_header: bool,
    *,
    fill_char: str = SPACE,
    pre_line: str = EMPTY,
    inter_cell: str = (2 * SPACE),
    post_line: str = EMPTY,
) -> str:
    """
    Generate a header or data table row

    :param row_data: data with an entry for each column in the row
    :param is_header: True if writing a header cell, otherwise writing a data cell. This determines whether to
                      use header or data alignment settings as well as maximum lines to wrap.
    :param fill_char: character that fills remaining space in a cell. Defaults to space. If this is a tab,
                      then it will be converted to one space. (Cannot be a line breaking character)
    :param pre_line: string to print before each line of a row. This can be used for a left row border and
                     padding before the first cell's text. (Defaults to blank)
    :param inter_cell: string to print where two cells meet. This can be used for a border between cells and padding
                       between it and the 2 cells' text. (Defaults to 2 spaces)
    :param post_line: string to print after each line of a row. This can be used for padding after
                      the last cell's text and a right row border. (Defaults to blank)
    :return: row string
    :raises ValueError: if row_data isn't the same length as self.cols
    :raises TypeError: if fill_char is more than one character (not including ANSI style sequences)
    :raises ValueError: if fill_char, pre_line, inter_cell, or post_line contains an unprintable
             character like a newline
    """

    class Cell:
        """Inner class which represents a table cell"""

        def __init__(self) -> None:
            # Data in this cell split into individual lines
            self.lines: Deque[str] = deque()

            # Display width of this cell
            self.width = 0

    if len(row_data) != len(self.cols):
        raise ValueError("Length of row_data must match length of cols")

    # Replace tabs (tabs in data strings will be handled in _generate_cell_lines())
    fill_char = fill_char.replace('\t', SPACE)
    pre_line = pre_line.replace('\t', SPACE * self.tab_width)
    inter_cell = inter_cell.replace('\t', SPACE * self.tab_width)
    post_line = post_line.replace('\t', SPACE * self.tab_width)

    # Validate fill_char character count
    if len(ansi.strip_style(fill_char)) != 1:
        raise TypeError("Fill character must be exactly one character long")

    # Look for unprintable characters
    validation_dict = {'fill_char': fill_char, 'pre_line': pre_line, 'inter_cell': inter_cell, 'post_line': post_line}
    for key, val in validation_dict.items():
        if ansi.style_aware_wcswidth(val) == -1:
            raise ValueError(f"{key} contains an unprintable character")

    # Number of lines this row uses
    total_lines = 0

    # Generate the cells for this row
    cells = list()

    for col_index, col in enumerate(self.cols):
        cell = Cell()
        cell.lines, cell.width = self._generate_cell_lines(row_data[col_index], is_header, col, fill_char)
        cells.append(cell)
        total_lines = max(len(cell.lines), total_lines)

    row_buf = io.StringIO()

    # Vertically align each cell
    for cell_index, cell in enumerate(cells):
        col = self.cols[cell_index]
        vert_align = col.header_vert_align if is_header else col.data_vert_align

        # Check if this cell need vertical filler
        line_diff = total_lines - len(cell.lines)
        if line_diff == 0:
            continue

        # Add vertical filler lines
        padding_line = utils.align_left(EMPTY, fill_char=fill_char, width=cell.width)
        if vert_align == VerticalAlignment.TOP:
            to_top = 0
            to_bottom = line_diff
        elif vert_align == VerticalAlignment.MIDDLE:
            to_top = line_diff // 2
            to_bottom = line_diff - to_top
        else:
            to_top = line_diff
            to_bottom = 0

        for i in range(to_top):
            cell.lines.appendleft(padding_line)
        for i in range(to_bottom):
            cell.lines.append(padding_line)

    # Build this row one line at a time
    for line_index in range(total_lines):
        for cell_index, cell in enumerate(cells):
            if cell_index == 0:
                row_buf.write(pre_line)

            row_buf.write(cell.lines[line_index])

            if cell_index < len(self.cols) - 1:
                row_buf.write(inter_cell)
            if cell_index == len(self.cols) - 1:
                row_buf.write(post_line)

        # Add a newline if this is not the last line
        if line_index < total_lines - 1:
            row_buf.write('\n')

    return row_buf.getvalue()

apply_border_color

apply_border_color(value)

If defined, apply the border foreground and background colors

PARAMETER DESCRIPTION
value

object whose text is to be colored

TYPE: Any

RETURNS DESCRIPTION
str

formatted text

Source code in cmd2/table_creator.py
def apply_border_color(self, value: Any) -> str:
    """
    If defined, apply the border foreground and background colors
    :param value: object whose text is to be colored
    :return: formatted text
    """
    if self.border_fg is None and self.border_bg is None:
        return str(value)
    return ansi.style(value, fg=self.border_fg, bg=self.border_bg)

apply_header_bg

apply_header_bg(value)

If defined, apply the header background color to header text

PARAMETER DESCRIPTION
value

object whose text is to be colored

TYPE: Any

RETURNS DESCRIPTION
str

formatted text

Source code in cmd2/table_creator.py
def apply_header_bg(self, value: Any) -> str:
    """
    If defined, apply the header background color to header text
    :param value: object whose text is to be colored
    :return: formatted text
    """
    if self.header_bg is None:
        return str(value)
    return ansi.style(value, bg=self.header_bg)

apply_data_bg

apply_data_bg(value)

If defined, apply the data background color to data text

PARAMETER DESCRIPTION
value

object whose text is to be colored

TYPE: Any

RETURNS DESCRIPTION
str

formatted data string

Source code in cmd2/table_creator.py
def apply_data_bg(self, value: Any) -> str:
    """
    If defined, apply the data background color to data text
    :param value: object whose text is to be colored
    :return: formatted data string
    """
    if self.data_bg is None:
        return str(value)
    return ansi.style(value, bg=self.data_bg)

base_width classmethod

base_width(num_cols, *, column_borders=True, padding=1)

Utility method to calculate the display width required for a table before data is added to it. This is useful when determining how wide to make your columns to have a table be a specific width.

PARAMETER DESCRIPTION
num_cols

how many columns the table will have

TYPE: int

column_borders

if True, borders between columns will be included in the calculation (Defaults to True)

TYPE: bool DEFAULT: True

padding

number of spaces between text and left/right borders of cell

TYPE: int DEFAULT: 1

RETURNS DESCRIPTION
int

base width

RAISES DESCRIPTION
ValueError

if num_cols is less than 1

Source code in cmd2/table_creator.py
@classmethod
def base_width(cls, num_cols: int, *, column_borders: bool = True, padding: int = 1) -> int:
    """
    Utility method to calculate the display width required for a table before data is added to it.
    This is useful when determining how wide to make your columns to have a table be a specific width.

    :param num_cols: how many columns the table will have
    :param column_borders: if True, borders between columns will be included in the calculation (Defaults to True)
    :param padding: number of spaces between text and left/right borders of cell
    :return: base width
    :raises ValueError: if num_cols is less than 1
    """
    if num_cols < 1:
        raise ValueError("Column count cannot be less than 1")

    data_str = SPACE
    data_width = ansi.style_aware_wcswidth(data_str) * num_cols

    tbl = cls([Column(data_str)] * num_cols, column_borders=column_borders, padding=padding)
    data_row = tbl.generate_data_row([data_str] * num_cols)

    return ansi.style_aware_wcswidth(data_row) - data_width

total_width

total_width()

Calculate the total display width of this table

Source code in cmd2/table_creator.py
def total_width(self) -> int:
    """Calculate the total display width of this table"""
    base_width = self.base_width(len(self.cols), column_borders=self.column_borders, padding=self.padding)
    data_width = sum(col.width for col in self.cols)
    return base_width + data_width

generate_table_top_border

generate_table_top_border()

Generate a border which appears at the top of the header and data section

Source code in cmd2/table_creator.py
def generate_table_top_border(self) -> str:
    """Generate a border which appears at the top of the header and data section"""
    fill_char = '═'

    pre_line = '╔' + self.padding * '═'

    inter_cell = self.padding * '═'
    if self.column_borders:
        inter_cell += "╤"
    inter_cell += self.padding * '═'

    post_line = self.padding * '═' + '╗'

    return self.generate_row(
        self.empty_data,
        is_header=False,
        fill_char=self.apply_border_color(fill_char),
        pre_line=self.apply_border_color(pre_line),
        inter_cell=self.apply_border_color(inter_cell),
        post_line=self.apply_border_color(post_line),
    )

generate_header_bottom_border

generate_header_bottom_border()

Generate a border which appears at the bottom of the header

Source code in cmd2/table_creator.py
def generate_header_bottom_border(self) -> str:
    """Generate a border which appears at the bottom of the header"""
    fill_char = '═'

    pre_line = '╠' + self.padding * '═'

    inter_cell = self.padding * '═'
    if self.column_borders:
        inter_cell += '╪'
    inter_cell += self.padding * '═'

    post_line = self.padding * '═' + '╣'

    return self.generate_row(
        self.empty_data,
        is_header=False,
        fill_char=self.apply_border_color(fill_char),
        pre_line=self.apply_border_color(pre_line),
        inter_cell=self.apply_border_color(inter_cell),
        post_line=self.apply_border_color(post_line),
    )

generate_row_bottom_border

generate_row_bottom_border()

Generate a border which appears at the bottom of rows

Source code in cmd2/table_creator.py
def generate_row_bottom_border(self) -> str:
    """Generate a border which appears at the bottom of rows"""
    fill_char = '─'

    pre_line = '╟' + self.padding * '─'

    inter_cell = self.padding * '─'
    if self.column_borders:
        inter_cell += '┼'
    inter_cell += self.padding * '─'
    inter_cell = inter_cell

    post_line = self.padding * '─' + '╢'

    return self.generate_row(
        self.empty_data,
        is_header=False,
        fill_char=self.apply_border_color(fill_char),
        pre_line=self.apply_border_color(pre_line),
        inter_cell=self.apply_border_color(inter_cell),
        post_line=self.apply_border_color(post_line),
    )

generate_table_bottom_border

generate_table_bottom_border()

Generate a border which appears at the bottom of the table

Source code in cmd2/table_creator.py
def generate_table_bottom_border(self) -> str:
    """Generate a border which appears at the bottom of the table"""
    fill_char = '═'

    pre_line = '╚' + self.padding * '═'

    inter_cell = self.padding * '═'
    if self.column_borders:
        inter_cell += '╧'
    inter_cell += self.padding * '═'

    post_line = self.padding * '═' + '╝'

    return self.generate_row(
        self.empty_data,
        is_header=False,
        fill_char=self.apply_border_color(fill_char),
        pre_line=self.apply_border_color(pre_line),
        inter_cell=self.apply_border_color(inter_cell),
        post_line=self.apply_border_color(post_line),
    )

generate_header

generate_header()

Generate table header

Source code in cmd2/table_creator.py
def generate_header(self) -> str:
    """Generate table header"""
    fill_char = self.apply_header_bg(SPACE)

    pre_line = self.apply_border_color('║') + self.apply_header_bg(self.padding * SPACE)

    inter_cell = self.apply_header_bg(self.padding * SPACE)
    if self.column_borders:
        inter_cell += self.apply_border_color('│')
    inter_cell += self.apply_header_bg(self.padding * SPACE)

    post_line = self.apply_header_bg(self.padding * SPACE) + self.apply_border_color('║')

    # Apply background color to header text in Columns which allow it
    to_display: List[Any] = []
    for col in self.cols:
        if col.style_header_text:
            to_display.append(self.apply_header_bg(col.header))
        else:
            to_display.append(col.header)

    # Create the bordered header
    header_buf = io.StringIO()
    header_buf.write(self.generate_table_top_border())
    header_buf.write('\n')
    header_buf.write(
        self.generate_row(
            to_display, is_header=True, fill_char=fill_char, pre_line=pre_line, inter_cell=inter_cell, post_line=post_line
        )
    )
    header_buf.write('\n')
    header_buf.write(self.generate_header_bottom_border())

    return header_buf.getvalue()

generate_data_row

generate_data_row(row_data)

Generate a data row

PARAMETER DESCRIPTION
row_data

data with an entry for each column in the row

TYPE: Sequence[Any]

RETURNS DESCRIPTION
str

data row string

RAISES DESCRIPTION
ValueError

if row_data isn't the same length as self.cols

Source code in cmd2/table_creator.py
def generate_data_row(self, row_data: Sequence[Any]) -> str:
    """
    Generate a data row

    :param row_data: data with an entry for each column in the row
    :return: data row string
    :raises ValueError: if row_data isn't the same length as self.cols
    """
    if len(row_data) != len(self.cols):
        raise ValueError("Length of row_data must match length of cols")

    fill_char = self.apply_data_bg(SPACE)

    pre_line = self.apply_border_color('║') + self.apply_data_bg(self.padding * SPACE)

    inter_cell = self.apply_data_bg(self.padding * SPACE)
    if self.column_borders:
        inter_cell += self.apply_border_color('│')
    inter_cell += self.apply_data_bg(self.padding * SPACE)

    post_line = self.apply_data_bg(self.padding * SPACE) + self.apply_border_color('║')

    # Apply background color to data text in Columns which allow it
    to_display: List[Any] = []
    for index, col in enumerate(self.cols):
        if col.style_data_text:
            to_display.append(self.apply_data_bg(row_data[index]))
        else:
            to_display.append(row_data[index])

    return self.generate_row(
        to_display, is_header=False, fill_char=fill_char, pre_line=pre_line, inter_cell=inter_cell, post_line=post_line
    )

generate_table

generate_table(table_data, *, include_header=True)

Generate a table from a data set

PARAMETER DESCRIPTION
table_data

Data with an entry for each data row of the table. Each entry should have data for each column in the row.

TYPE: Sequence[Sequence[Any]]

include_header

If True, then a header will be included at top of table. (Defaults to True)

TYPE: bool DEFAULT: True

Source code in cmd2/table_creator.py
def generate_table(self, table_data: Sequence[Sequence[Any]], *, include_header: bool = True) -> str:
    """
    Generate a table from a data set

    :param table_data: Data with an entry for each data row of the table. Each entry should have data for
                       each column in the row.
    :param include_header: If True, then a header will be included at top of table. (Defaults to True)
    """
    table_buf = io.StringIO()

    if include_header:
        header = self.generate_header()
        table_buf.write(header)
    else:
        top_border = self.generate_table_top_border()
        table_buf.write(top_border)

    table_buf.write('\n')

    for index, row_data in enumerate(table_data):
        if index > 0:
            row_bottom_border = self.generate_row_bottom_border()
            table_buf.write(row_bottom_border)
            table_buf.write('\n')

        row = self.generate_data_row(row_data)
        table_buf.write(row)
        table_buf.write('\n')

    table_buf.write(self.generate_table_bottom_border())
    return table_buf.getvalue()

AlternatingTable

AlternatingTable(cols, *, tab_width=4, column_borders=True, padding=1, border_fg=None, border_bg=None, header_bg=None, odd_bg=None, even_bg=DARK_GRAY)

Bases: BorderedTable

Implementation of BorderedTable which uses background colors to distinguish between rows instead of row border lines. This class can be used to create the whole table at once or one row at a time.

To nest an AlternatingTable within another AlternatingTable, set style_data_text to False on the Column which contains the nested table. That will prevent the current row's background color from affecting the colors of the nested table.

AlternatingTable initializer

Note: Specify background colors using subclasses of BgColor (e.g. Bg, EightBitBg, RgbBg)

PARAMETER DESCRIPTION
cols

column definitions for this table

TYPE: Sequence[Column]

tab_width

all tabs will be replaced with this many spaces. If a row's fill_char is a tab, then it will be converted to one space.

TYPE: int DEFAULT: 4

column_borders

if True, borders between columns will be included. This gives the table a grid-like appearance. Turning off column borders results in a unified appearance between a row's cells. (Defaults to True)

TYPE: bool DEFAULT: True

padding

number of spaces between text and left/right borders of cell

TYPE: int DEFAULT: 1

border_fg

optional foreground color for borders (defaults to None)

TYPE: Optional[FgColor] DEFAULT: None

border_bg

optional background color for borders (defaults to None)

TYPE: Optional[BgColor] DEFAULT: None

header_bg

optional background color for header cells (defaults to None)

TYPE: Optional[BgColor] DEFAULT: None

odd_bg

optional background color for odd numbered data rows (defaults to None)

TYPE: Optional[BgColor] DEFAULT: None

even_bg

optional background color for even numbered data rows (defaults to StdBg.DARK_GRAY)

TYPE: Optional[BgColor] DEFAULT: DARK_GRAY

RAISES DESCRIPTION
ValueError

if tab_width is less than 1

ValueError

if padding is less than 0

Source code in cmd2/table_creator.py
def __init__(
    self,
    cols: Sequence[Column],
    *,
    tab_width: int = 4,
    column_borders: bool = True,
    padding: int = 1,
    border_fg: Optional[ansi.FgColor] = None,
    border_bg: Optional[ansi.BgColor] = None,
    header_bg: Optional[ansi.BgColor] = None,
    odd_bg: Optional[ansi.BgColor] = None,
    even_bg: Optional[ansi.BgColor] = ansi.Bg.DARK_GRAY,
) -> None:
    """
    AlternatingTable initializer

    Note: Specify background colors using subclasses of BgColor (e.g. Bg, EightBitBg, RgbBg)

    :param cols: column definitions for this table
    :param tab_width: all tabs will be replaced with this many spaces. If a row's fill_char is a tab,
                      then it will be converted to one space.
    :param column_borders: if True, borders between columns will be included. This gives the table a grid-like
                           appearance. Turning off column borders results in a unified appearance between
                           a row's cells. (Defaults to True)
    :param padding: number of spaces between text and left/right borders of cell
    :param border_fg: optional foreground color for borders (defaults to None)
    :param border_bg: optional background color for borders (defaults to None)
    :param header_bg: optional background color for header cells (defaults to None)
    :param odd_bg: optional background color for odd numbered data rows (defaults to None)
    :param even_bg: optional background color for even numbered data rows (defaults to StdBg.DARK_GRAY)
    :raises ValueError: if tab_width is less than 1
    :raises ValueError: if padding is less than 0
    """
    super().__init__(
        cols,
        tab_width=tab_width,
        column_borders=column_borders,
        padding=padding,
        border_fg=border_fg,
        border_bg=border_bg,
        header_bg=header_bg,
    )
    self.row_num = 1
    self.odd_bg = odd_bg
    self.even_bg = even_bg

cols instance-attribute

cols = copy(cols)

tab_width instance-attribute

tab_width = tab_width

empty_data instance-attribute

empty_data = [EMPTY] * len(cols)

column_borders instance-attribute

column_borders = column_borders

padding instance-attribute

padding = padding

border_fg instance-attribute

border_fg = border_fg

border_bg instance-attribute

border_bg = border_bg

header_bg instance-attribute

header_bg = header_bg

data_bg instance-attribute

data_bg = data_bg

row_num instance-attribute

row_num = 1

odd_bg instance-attribute

odd_bg = odd_bg

even_bg instance-attribute

even_bg = even_bg

generate_row

generate_row(row_data, is_header, *, fill_char=SPACE, pre_line=EMPTY, inter_cell=2 * SPACE, post_line=EMPTY)

Generate a header or data table row

PARAMETER DESCRIPTION
row_data

data with an entry for each column in the row

TYPE: Sequence[Any]

is_header

True if writing a header cell, otherwise writing a data cell. This determines whether to use header or data alignment settings as well as maximum lines to wrap.

TYPE: bool

fill_char

character that fills remaining space in a cell. Defaults to space. If this is a tab, then it will be converted to one space. (Cannot be a line breaking character)

TYPE: str DEFAULT: SPACE

pre_line

string to print before each line of a row. This can be used for a left row border and padding before the first cell's text. (Defaults to blank)

TYPE: str DEFAULT: EMPTY

inter_cell

string to print where two cells meet. This can be used for a border between cells and padding between it and the 2 cells' text. (Defaults to 2 spaces)

TYPE: str DEFAULT: 2 * SPACE

post_line

string to print after each line of a row. This can be used for padding after the last cell's text and a right row border. (Defaults to blank)

TYPE: str DEFAULT: EMPTY

RETURNS DESCRIPTION
str

row string

RAISES DESCRIPTION
ValueError

if row_data isn't the same length as self.cols

TypeError

if fill_char is more than one character (not including ANSI style sequences)

ValueError

if fill_char, pre_line, inter_cell, or post_line contains an unprintable character like a newline

Source code in cmd2/table_creator.py
def generate_row(
    self,
    row_data: Sequence[Any],
    is_header: bool,
    *,
    fill_char: str = SPACE,
    pre_line: str = EMPTY,
    inter_cell: str = (2 * SPACE),
    post_line: str = EMPTY,
) -> str:
    """
    Generate a header or data table row

    :param row_data: data with an entry for each column in the row
    :param is_header: True if writing a header cell, otherwise writing a data cell. This determines whether to
                      use header or data alignment settings as well as maximum lines to wrap.
    :param fill_char: character that fills remaining space in a cell. Defaults to space. If this is a tab,
                      then it will be converted to one space. (Cannot be a line breaking character)
    :param pre_line: string to print before each line of a row. This can be used for a left row border and
                     padding before the first cell's text. (Defaults to blank)
    :param inter_cell: string to print where two cells meet. This can be used for a border between cells and padding
                       between it and the 2 cells' text. (Defaults to 2 spaces)
    :param post_line: string to print after each line of a row. This can be used for padding after
                      the last cell's text and a right row border. (Defaults to blank)
    :return: row string
    :raises ValueError: if row_data isn't the same length as self.cols
    :raises TypeError: if fill_char is more than one character (not including ANSI style sequences)
    :raises ValueError: if fill_char, pre_line, inter_cell, or post_line contains an unprintable
             character like a newline
    """

    class Cell:
        """Inner class which represents a table cell"""

        def __init__(self) -> None:
            # Data in this cell split into individual lines
            self.lines: Deque[str] = deque()

            # Display width of this cell
            self.width = 0

    if len(row_data) != len(self.cols):
        raise ValueError("Length of row_data must match length of cols")

    # Replace tabs (tabs in data strings will be handled in _generate_cell_lines())
    fill_char = fill_char.replace('\t', SPACE)
    pre_line = pre_line.replace('\t', SPACE * self.tab_width)
    inter_cell = inter_cell.replace('\t', SPACE * self.tab_width)
    post_line = post_line.replace('\t', SPACE * self.tab_width)

    # Validate fill_char character count
    if len(ansi.strip_style(fill_char)) != 1:
        raise TypeError("Fill character must be exactly one character long")

    # Look for unprintable characters
    validation_dict = {'fill_char': fill_char, 'pre_line': pre_line, 'inter_cell': inter_cell, 'post_line': post_line}
    for key, val in validation_dict.items():
        if ansi.style_aware_wcswidth(val) == -1:
            raise ValueError(f"{key} contains an unprintable character")

    # Number of lines this row uses
    total_lines = 0

    # Generate the cells for this row
    cells = list()

    for col_index, col in enumerate(self.cols):
        cell = Cell()
        cell.lines, cell.width = self._generate_cell_lines(row_data[col_index], is_header, col, fill_char)
        cells.append(cell)
        total_lines = max(len(cell.lines), total_lines)

    row_buf = io.StringIO()

    # Vertically align each cell
    for cell_index, cell in enumerate(cells):
        col = self.cols[cell_index]
        vert_align = col.header_vert_align if is_header else col.data_vert_align

        # Check if this cell need vertical filler
        line_diff = total_lines - len(cell.lines)
        if line_diff == 0:
            continue

        # Add vertical filler lines
        padding_line = utils.align_left(EMPTY, fill_char=fill_char, width=cell.width)
        if vert_align == VerticalAlignment.TOP:
            to_top = 0
            to_bottom = line_diff
        elif vert_align == VerticalAlignment.MIDDLE:
            to_top = line_diff // 2
            to_bottom = line_diff - to_top
        else:
            to_top = line_diff
            to_bottom = 0

        for i in range(to_top):
            cell.lines.appendleft(padding_line)
        for i in range(to_bottom):
            cell.lines.append(padding_line)

    # Build this row one line at a time
    for line_index in range(total_lines):
        for cell_index, cell in enumerate(cells):
            if cell_index == 0:
                row_buf.write(pre_line)

            row_buf.write(cell.lines[line_index])

            if cell_index < len(self.cols) - 1:
                row_buf.write(inter_cell)
            if cell_index == len(self.cols) - 1:
                row_buf.write(post_line)

        # Add a newline if this is not the last line
        if line_index < total_lines - 1:
            row_buf.write('\n')

    return row_buf.getvalue()

apply_border_color

apply_border_color(value)

If defined, apply the border foreground and background colors

PARAMETER DESCRIPTION
value

object whose text is to be colored

TYPE: Any

RETURNS DESCRIPTION
str

formatted text

Source code in cmd2/table_creator.py
def apply_border_color(self, value: Any) -> str:
    """
    If defined, apply the border foreground and background colors
    :param value: object whose text is to be colored
    :return: formatted text
    """
    if self.border_fg is None and self.border_bg is None:
        return str(value)
    return ansi.style(value, fg=self.border_fg, bg=self.border_bg)

apply_header_bg

apply_header_bg(value)

If defined, apply the header background color to header text

PARAMETER DESCRIPTION
value

object whose text is to be colored

TYPE: Any

RETURNS DESCRIPTION
str

formatted text

Source code in cmd2/table_creator.py
def apply_header_bg(self, value: Any) -> str:
    """
    If defined, apply the header background color to header text
    :param value: object whose text is to be colored
    :return: formatted text
    """
    if self.header_bg is None:
        return str(value)
    return ansi.style(value, bg=self.header_bg)

base_width classmethod

base_width(num_cols, *, column_borders=True, padding=1)

Utility method to calculate the display width required for a table before data is added to it. This is useful when determining how wide to make your columns to have a table be a specific width.

PARAMETER DESCRIPTION
num_cols

how many columns the table will have

TYPE: int

column_borders

if True, borders between columns will be included in the calculation (Defaults to True)

TYPE: bool DEFAULT: True

padding

number of spaces between text and left/right borders of cell

TYPE: int DEFAULT: 1

RETURNS DESCRIPTION
int

base width

RAISES DESCRIPTION
ValueError

if num_cols is less than 1

Source code in cmd2/table_creator.py
@classmethod
def base_width(cls, num_cols: int, *, column_borders: bool = True, padding: int = 1) -> int:
    """
    Utility method to calculate the display width required for a table before data is added to it.
    This is useful when determining how wide to make your columns to have a table be a specific width.

    :param num_cols: how many columns the table will have
    :param column_borders: if True, borders between columns will be included in the calculation (Defaults to True)
    :param padding: number of spaces between text and left/right borders of cell
    :return: base width
    :raises ValueError: if num_cols is less than 1
    """
    if num_cols < 1:
        raise ValueError("Column count cannot be less than 1")

    data_str = SPACE
    data_width = ansi.style_aware_wcswidth(data_str) * num_cols

    tbl = cls([Column(data_str)] * num_cols, column_borders=column_borders, padding=padding)
    data_row = tbl.generate_data_row([data_str] * num_cols)

    return ansi.style_aware_wcswidth(data_row) - data_width

total_width

total_width()

Calculate the total display width of this table

Source code in cmd2/table_creator.py
def total_width(self) -> int:
    """Calculate the total display width of this table"""
    base_width = self.base_width(len(self.cols), column_borders=self.column_borders, padding=self.padding)
    data_width = sum(col.width for col in self.cols)
    return base_width + data_width

generate_table_top_border

generate_table_top_border()

Generate a border which appears at the top of the header and data section

Source code in cmd2/table_creator.py
def generate_table_top_border(self) -> str:
    """Generate a border which appears at the top of the header and data section"""
    fill_char = '═'

    pre_line = '╔' + self.padding * '═'

    inter_cell = self.padding * '═'
    if self.column_borders:
        inter_cell += "╤"
    inter_cell += self.padding * '═'

    post_line = self.padding * '═' + '╗'

    return self.generate_row(
        self.empty_data,
        is_header=False,
        fill_char=self.apply_border_color(fill_char),
        pre_line=self.apply_border_color(pre_line),
        inter_cell=self.apply_border_color(inter_cell),
        post_line=self.apply_border_color(post_line),
    )

generate_header_bottom_border

generate_header_bottom_border()

Generate a border which appears at the bottom of the header

Source code in cmd2/table_creator.py
def generate_header_bottom_border(self) -> str:
    """Generate a border which appears at the bottom of the header"""
    fill_char = '═'

    pre_line = '╠' + self.padding * '═'

    inter_cell = self.padding * '═'
    if self.column_borders:
        inter_cell += '╪'
    inter_cell += self.padding * '═'

    post_line = self.padding * '═' + '╣'

    return self.generate_row(
        self.empty_data,
        is_header=False,
        fill_char=self.apply_border_color(fill_char),
        pre_line=self.apply_border_color(pre_line),
        inter_cell=self.apply_border_color(inter_cell),
        post_line=self.apply_border_color(post_line),
    )

generate_row_bottom_border

generate_row_bottom_border()

Generate a border which appears at the bottom of rows

Source code in cmd2/table_creator.py
def generate_row_bottom_border(self) -> str:
    """Generate a border which appears at the bottom of rows"""
    fill_char = '─'

    pre_line = '╟' + self.padding * '─'

    inter_cell = self.padding * '─'
    if self.column_borders:
        inter_cell += '┼'
    inter_cell += self.padding * '─'
    inter_cell = inter_cell

    post_line = self.padding * '─' + '╢'

    return self.generate_row(
        self.empty_data,
        is_header=False,
        fill_char=self.apply_border_color(fill_char),
        pre_line=self.apply_border_color(pre_line),
        inter_cell=self.apply_border_color(inter_cell),
        post_line=self.apply_border_color(post_line),
    )

generate_table_bottom_border

generate_table_bottom_border()

Generate a border which appears at the bottom of the table

Source code in cmd2/table_creator.py
def generate_table_bottom_border(self) -> str:
    """Generate a border which appears at the bottom of the table"""
    fill_char = '═'

    pre_line = '╚' + self.padding * '═'

    inter_cell = self.padding * '═'
    if self.column_borders:
        inter_cell += '╧'
    inter_cell += self.padding * '═'

    post_line = self.padding * '═' + '╝'

    return self.generate_row(
        self.empty_data,
        is_header=False,
        fill_char=self.apply_border_color(fill_char),
        pre_line=self.apply_border_color(pre_line),
        inter_cell=self.apply_border_color(inter_cell),
        post_line=self.apply_border_color(post_line),
    )

generate_header

generate_header()

Generate table header

Source code in cmd2/table_creator.py
def generate_header(self) -> str:
    """Generate table header"""
    fill_char = self.apply_header_bg(SPACE)

    pre_line = self.apply_border_color('║') + self.apply_header_bg(self.padding * SPACE)

    inter_cell = self.apply_header_bg(self.padding * SPACE)
    if self.column_borders:
        inter_cell += self.apply_border_color('│')
    inter_cell += self.apply_header_bg(self.padding * SPACE)

    post_line = self.apply_header_bg(self.padding * SPACE) + self.apply_border_color('║')

    # Apply background color to header text in Columns which allow it
    to_display: List[Any] = []
    for col in self.cols:
        if col.style_header_text:
            to_display.append(self.apply_header_bg(col.header))
        else:
            to_display.append(col.header)

    # Create the bordered header
    header_buf = io.StringIO()
    header_buf.write(self.generate_table_top_border())
    header_buf.write('\n')
    header_buf.write(
        self.generate_row(
            to_display, is_header=True, fill_char=fill_char, pre_line=pre_line, inter_cell=inter_cell, post_line=post_line
        )
    )
    header_buf.write('\n')
    header_buf.write(self.generate_header_bottom_border())

    return header_buf.getvalue()

apply_data_bg

apply_data_bg(value)

Apply background color to data text based on what row is being generated and whether a color has been defined

PARAMETER DESCRIPTION
value

object whose text is to be colored

TYPE: Any

RETURNS DESCRIPTION
str

formatted data string

Source code in cmd2/table_creator.py
def apply_data_bg(self, value: Any) -> str:
    """
    Apply background color to data text based on what row is being generated and whether a color has been defined
    :param value: object whose text is to be colored
    :return: formatted data string
    """
    if self.row_num % 2 == 0 and self.even_bg is not None:
        return ansi.style(value, bg=self.even_bg)
    elif self.row_num % 2 != 0 and self.odd_bg is not None:
        return ansi.style(value, bg=self.odd_bg)
    else:
        return str(value)

generate_data_row

generate_data_row(row_data)

Generate a data row

PARAMETER DESCRIPTION
row_data

data with an entry for each column in the row

TYPE: Sequence[Any]

RETURNS DESCRIPTION
str

data row string

Source code in cmd2/table_creator.py
def generate_data_row(self, row_data: Sequence[Any]) -> str:
    """
    Generate a data row

    :param row_data: data with an entry for each column in the row
    :return: data row string
    """
    row = super().generate_data_row(row_data)
    self.row_num += 1
    return row

generate_table

generate_table(table_data, *, include_header=True)

Generate a table from a data set

PARAMETER DESCRIPTION
table_data

Data with an entry for each data row of the table. Each entry should have data for each column in the row.

TYPE: Sequence[Sequence[Any]]

include_header

If True, then a header will be included at top of table. (Defaults to True)

TYPE: bool DEFAULT: True

Source code in cmd2/table_creator.py
def generate_table(self, table_data: Sequence[Sequence[Any]], *, include_header: bool = True) -> str:
    """
    Generate a table from a data set

    :param table_data: Data with an entry for each data row of the table. Each entry should have data for
                       each column in the row.
    :param include_header: If True, then a header will be included at top of table. (Defaults to True)
    """
    table_buf = io.StringIO()

    if include_header:
        header = self.generate_header()
        table_buf.write(header)
    else:
        top_border = self.generate_table_top_border()
        table_buf.write(top_border)

    table_buf.write('\n')

    for row_data in table_data:
        row = self.generate_data_row(row_data)
        table_buf.write(row)
        table_buf.write('\n')

    table_buf.write(self.generate_table_bottom_border())
    return table_buf.getvalue()