Source code for tabled.pretty_print

"""
tabled.pretty_print
~~~~~~~~~~~~~~~~~~~

:synopsis: Pretty printing engine for tableD.
:copyright: (c) 2017, Tommy Ip.
:license: MIT
"""

from typing import Dict, List, Text, Optional

from .style_templates import get_style
from .utils import columns_width


[docs]def left_pad(string: Text, width: int) -> Text: """ Insert spaces to the left of a string to fit into a container. Args: string: A text value to be padded with spaces. width: The width of the string container. Returns: A string aligned right in a container. Example: >>> left_pad('tableD', 10) ' tableD' """ pad_amount = width - len(string) if pad_amount < 0: # Container too narrow raise ValueError('The input string is longer than the allowed width.') return "{}{}".format(" " * pad_amount, string)
[docs]def right_pad(string: Text, width: int) -> Text: """ Insert spaces to the right of a string to fit into a container. Args: string: A text value to be padded with spaces. width: The width of the string container. Returns: A string aligned left in a container. Example: >>> right_pad('tableD', 10) 'tableD ' """ pad_amount = width - len(string) if pad_amount < 0: # Container too narrow raise ValueError('The input string is longer than the allowed width.') return "{}{}".format(string, " " * pad_amount)
[docs]def left_right_pad(string: Text, width: int) -> Text: """ Insert spaces to both sides of a string to fit into a container. Note: The right side of the string would be allocated more spaces if the amount of blank spaces cannot be divided equally by 2. Args: string: A text value to be padded with spaces. width: The width of the string container. Returns: A string aligned center in a container. Example: >>> left_right_pad('tableD', 11) ' tableD ' """ string_length = len(string) pad_amount = width - string_length if pad_amount < 0: # Container too narrow raise ValueError('The input string is longer than the allowed width.') left_padded = left_pad(string, (pad_amount // 2) + string_length) return right_pad(left_padded, width)
[docs]def pad(string: Text, width: int, align: Text, margin: int = 1) -> Text: """ Pad and align a string in a container. Args: string: Input text to be padded and aligned. width: The width of the container. align: Left, center or right alignment. margin: Margin width between the string and the side wall. Returns: A string padded and aligned in a container. Example: >>> pad('library', 13, 'left') ' library ' """ width_no_margin = width - (margin * 2) # Case/switch hack to reduce if/elif cluster. pad_string = { 'left': right_pad, 'right': left_pad, 'center': left_right_pad } padded = pad_string[align](string, width_no_margin) return left_right_pad(padded, len(padded) + (margin * 2))
[docs]def render_row(row: List[Text], widths: List[int], delimiters: Dict[str, Text], align: Text = 'left', margin: int = 1) -> Text: """ Render a table row. Args: row: A row of string, each is a cell of their columns. widths: A list of column widths. delimiter: A dictionary of column dividers. align: Left, center or right alignment of each cell. margin: Margin width between the string and the side wall. Returns: A string containing a print ready row. Example: >>> render_row(['Some cell content', 'word', '1'], [22, 6, 7], ... {'left': '|', 'right': '|', 'connector': '|'}) '| Some cell content | word | 1 |' """ output = '{left_wall}{columns}{right_wall}' padded_row = [pad(*cell_n_width, align=align, margin=margin) for cell_n_width in zip(row, widths)] return output.format( left_wall=delimiters['left'], columns=delimiters['connector'].join(padded_row), right_wall=delimiters['right'] )
[docs]def render_table(headings: List[Text], table: List[List[Text]], style: Text = 'default', align: Optional[Text] = None) -> Text: """ This is where the magic happens! Args: headings: A list of text containing the headings. table: Cells data in a nested list of lists structure. style: Style of formatting in the table. align: Override settings in style if specified. Returns: A string with formatting ready for output. Example: >>> table = [['1', '1'], ... ['2', '4'], ... ['3', '9']] >>> print(render_table(['x', 'f(x) = x^2'], table)) +---+------------+ | x | f(x) = x^2 | +---+------------+ | 1 | 1 | | 2 | 4 | | 3 | 9 | +---+------------+ """ styling = get_style(style) if not align: # Override alignment specified in styles. align = styling['raw']['align'] widths = [(width + 2) for width in columns_width([headings] + table)] divider = [''.join(styling['raw']['horizontal']) * width for width in widths] rows = [ # Top border render_row(divider, widths, styling['top_border'], margin=0), # Headings render_row(headings, widths, styling['row'], align=align), # Heading/body divider render_row(divider, widths, styling['divider'], margin=0), # Actual table body '\n'.join([render_row(row, widths, styling['row'], align=align) for row in table]), # Bottom border render_row(divider, widths, styling['bottom_border'], margin=0) ] return '\n'.join(rows)