Giter VIP home page Giter VIP logo

txtble's Introduction

Project Status: Active — The project has reached a stable, usable state and is being actively developed. CI Status coverage pyversions MIT License

GitHub | PyPI | Issues | Changelog

txtble is yet another Python library for creating plain-text tables. (All the good names were taken, OK?) Pass in a list of lists of strings (or other stringable things) and get out something nice like:

+---------+----------+------------------+
|Month    |Birthstone|Birth Flower      |
+---------+----------+------------------+
|January  |Garnet    |Carnation         |
|February |Amethyst  |Violet            |
|March    |Aquamarine|Jonquil           |
|April    |Diamond   |Sweetpea          |
|May      |Emerald   |Lily Of The Valley|
|June     |Pearl     |Rose              |
|July     |Ruby      |Larkspur          |
|August   |Peridot   |Gladiolus         |
|September|Sapphire  |Aster             |
|October  |Opal      |Calendula         |
|November |Topaz     |Chrysanthemum     |
|December |Turquoise |Narcissus         |
+---------+----------+------------------+

Features:

  • Rows can be passed as lists or dicts
  • ANSI color aware
  • Unicode fullwidth & combining character aware
  • Control the horizontal (left, center, & right) and vertical (top, middle, & bottom) alignment of text
  • Align numbers along their decimal points
  • Customize characters used for drawing borders
  • Toggle inter-row, inter-column, and outer borders
  • Set the value used to fill out ragged rows
  • Pad cells on the left & right
  • Set column widths, with long lines wrapped to fit
  • Configure how None values are displayed
  • Type annotations

txtble requires Python 3.8 or higher. Just use pip for Python 3 (You have pip, right?) to install txtble and its dependencies:

python3 -m pip install txtble

Construct & show a basic table:

>>> from txtble import Txtble
>>> # Taken from /usr/share/misc/birthtoken.gz in Ubuntu Xenial's miscfiles package:
>>> HEADERS = ['Month', 'Birthstone', 'Birth Flower']
>>> DATA = [
...     ['January',   'Garnet',     'Carnation'],
...     ['February',  'Amethyst',   'Violet'],
...     ['March',     'Aquamarine', 'Jonquil'],
...     ['April',     'Diamond',    'Sweetpea'],
...     ['May',       'Emerald',    'Lily Of The Valley'],
...     ['June',      'Pearl',      'Rose'],
...     ['July',      'Ruby',       'Larkspur'],
...     ['August',    'Peridot',    'Gladiolus'],
...     ['September', 'Sapphire',   'Aster'],
...     ['October',   'Opal',       'Calendula'],
...     ['November',  'Topaz',      'Chrysanthemum'],
...     ['December',  'Turquoise',  'Narcissus'],
... ]
>>> tbl = Txtble(DATA, headers=HEADERS)
>>> print(tbl)
+---------+----------+------------------+
|Month    |Birthstone|Birth Flower      |
+---------+----------+------------------+
|January  |Garnet    |Carnation         |
|February |Amethyst  |Violet            |
|March    |Aquamarine|Jonquil           |
|April    |Diamond   |Sweetpea          |
|May      |Emerald   |Lily Of The Valley|
|June     |Pearl     |Rose              |
|July     |Ruby      |Larkspur          |
|August   |Peridot   |Gladiolus         |
|September|Sapphire  |Aster             |
|October  |Opal      |Calendula         |
|November |Topaz     |Chrysanthemum     |
|December |Turquoise |Narcissus         |
+---------+----------+------------------+

The table can also be constructed like this:

>>> tbl = Txtble(headers=HEADERS)
>>> tbl.extend(DATA)

Or like this:

>>> tbl = Txtble(headers=HEADERS)
>>> for row in DATA:
...     tbl.append(row)

Or even like this:

>>> tbl = Txtble(DATA)
>>> tbl.headers = HEADERS

The rows of the table can be lists of values (as seen above) or dicts that map header names to values:

>>> tbl = Txtble(
...     headers = ["Red", "Green", "Blue"],
...     data    = [
...         {"Red": "Ruby", "Green": "Emerald", "Blue": "Sapphire"},
...         {"Red": "Fire", "Green": "Earth",   "Blue": "Water"},
...     ],
... )
>>> print(tbl)
+----+-------+--------+
|Red |Green  |Blue    |
+----+-------+--------+
|Ruby|Emerald|Sapphire|
|Fire|Earth  |Water   |
+----+-------+--------+

Missing dict keys can be filled in with the dict_fill option (Without it, you'd get a KeyError here):

>>> tbl = Txtble(
...     headers = ["Red", "Green", "Blue"],
...     data    = [
...         {"Red": "Ruby", "Green": "Emerald", "Blue": "Sapphire"},
...         {"Red": "Fire", "Green": "Earth",   "Blue": "Water"},
...         {"Red": "Hot",                      "Blue": "Cold"},
...     ],
...     dict_fill = 'UNKNOWN',
... )
>>> print(tbl)
+----+-------+--------+
|Red |Green  |Blue    |
+----+-------+--------+
|Ruby|Emerald|Sapphire|
|Fire|Earth  |Water   |
|Hot |UNKNOWN|Cold    |
+----+-------+--------+

The number of columns is automatically set to the length of the longest row:

>>> tbl = Txtble([
...     ['1', '1'],
...     ['Z_6', '1', 'x', 'x^2', 'x^3', 'x^4', 'x^5'],
...     ['S_3', '1', 'a', 'b', 'aba', 'ba', 'ab'],
...     ['Z_4', '1', 'x', 'x^2', 'x^3'],
...     ['V_4', '1', 'a', 'b', 'ab'],
... ])
>>> print(tbl)
+---+-+-+---+---+---+---+
|1  |1| |   |   |   |   |
|Z_6|1|x|x^2|x^3|x^4|x^5|
|S_3|1|a|b  |aba|ba |ab |
|Z_4|1|x|x^2|x^3|   |   |
|V_4|1|a|b  |ab |   |   |
+---+-+-+---+---+---+---+

... unless you've specified a header row, which puts a limit on the number of columns:

>>> tbl.headers = ['Group', 'Elements']
>>> print(tbl)
+-----+--------+
|Group|Elements|
+-----+--------+
|1    |1       |
|Z_6  |1       |
|S_3  |1       |
|Z_4  |1       |
|V_4  |1       |
+-----+--------+

... unless you've also specified a header_fill to use as the header for extra columns:

>>> tbl.header_fill = 'Extra!'
>>> print(tbl)
+-----+--------+------+------+------+------+------+
|Group|Elements|Extra!|Extra!|Extra!|Extra!|Extra!|
+-----+--------+------+------+------+------+------+
|1    |1       |      |      |      |      |      |
|Z_6  |1       |x     |x^2   |x^3   |x^4   |x^5   |
|S_3  |1       |a     |b     |aba   |ba    |ab    |
|Z_4  |1       |x     |x^2   |x^3   |      |      |
|V_4  |1       |a     |b     |ab    |      |      |
+-----+--------+------+------+------+------+------+

You can set the widths of columns; long lines will be wrapped to fit:

>>> tbl = Txtble(
...     headers=['Short Text', 'Long Text'],
...     data=[
...         [
...             'Hi there!',
...             'Lorem ipsum dolor sit amet, consectetur adipisicing elit',
...         ]
...     ],
...     widths=[20, 20],
... )
>>> print(tbl)
+--------------------+--------------------+
|Short Text          |Long Text           |
+--------------------+--------------------+
|Hi there!           |Lorem ipsum dolor   |
|                    |sit amet,           |
|                    |consectetur         |
|                    |adipisicing elit    |
+--------------------+--------------------+

You can align column text to the left, right, or center:

>>> tbl = Txtble(DATA, headers=HEADERS, align=['r', 'c', 'l'])
>>> print(tbl)
+---------+----------+------------------+
|    Month|Birthstone|Birth Flower      |
+---------+----------+------------------+
|  January|  Garnet  |Carnation         |
| February| Amethyst |Violet            |
|    March|Aquamarine|Jonquil           |
|    April| Diamond  |Sweetpea          |
|      May| Emerald  |Lily Of The Valley|
|     June|  Pearl   |Rose              |
|     July|   Ruby   |Larkspur          |
|   August| Peridot  |Gladiolus         |
|September| Sapphire |Aster             |
|  October|   Opal   |Calendula         |
| November|  Topaz   |Chrysanthemum     |
| December|Turquoise |Narcissus         |
+---------+----------+------------------+

Numbers in the same column can be aligned on their decimal point with the 'n' alignment:

>>> tbl = Txtble(
...     headers=['Thing', 'Value'],
...     data=[
...         ['Foo', 12345],
...         ['Bar', 1234.5],
...         ['Baz', 123.45],
...         ['Quux', 12.345],
...         ['Glarch', 1.2345],
...         ['Gnusto', .12345],
...     ],
...     align=['l', 'n'],
... )
>>> print(tbl)
+------+-----------+
|Thing |Value      |
+------+-----------+
|Foo   |12345      |
|Bar   | 1234.5    |
|Baz   |  123.45   |
|Quux  |   12.345  |
|Glarch|    1.2345 |
|Gnusto|    0.12345|
+------+-----------+

Unicode works too, even fullwidth characters and combining characters:

>>> tbl = Txtble(
...     headers=['Wide', 'Accented'],
...     data=[
...         [
...             u'\uFF37\uFF49\uFF44\uFF45',
...             u'A\u0301c\u0301c\u0301e\u0301n\u0301t\u0301e\u0301d\u0301',
...         ]
...     ]
... )
>>> print(tbl)
+--------+--------+
|Wide    |Accented|
+--------+--------+
|Wide|Áććéńt́éd́|
+--------+--------+

You can configure the borders and make them fancy:

>>> from txtble import ASCII_EQ_BORDERS
>>> tbl = Txtble(
...     DATA,
...     headers       = HEADERS,
...     header_border = ASCII_EQ_BORDERS,
...     row_border    = True,
... )
>>> print(tbl)
+---------+----------+------------------+
|Month    |Birthstone|Birth Flower      |
+=========+==========+==================+
|January  |Garnet    |Carnation         |
+---------+----------+------------------+
|February |Amethyst  |Violet            |
+---------+----------+------------------+
|March    |Aquamarine|Jonquil           |
+---------+----------+------------------+
|April    |Diamond   |Sweetpea          |
+---------+----------+------------------+
|May      |Emerald   |Lily Of The Valley|
+---------+----------+------------------+
|June     |Pearl     |Rose              |
+---------+----------+------------------+
|July     |Ruby      |Larkspur          |
+---------+----------+------------------+
|August   |Peridot   |Gladiolus         |
+---------+----------+------------------+
|September|Sapphire  |Aster             |
+---------+----------+------------------+
|October  |Opal      |Calendula         |
+---------+----------+------------------+
|November |Topaz     |Chrysanthemum     |
+---------+----------+------------------+
|December |Turquoise |Narcissus         |
+---------+----------+------------------+

... or very fancy:

>>> from txtble import DOUBLE_BORDERS
>>> tbl = Txtble(DATA, headers=HEADERS, border_style=DOUBLE_BORDERS)
>>> print(tbl)
╔═════════╦══════════╦══════════════════╗
║Month    ║Birthstone║Birth Flower      ║
╠═════════╬══════════╬══════════════════╣
║January  ║Garnet    ║Carnation         ║
║February ║Amethyst  ║Violet            ║
║March    ║Aquamarine║Jonquil           ║
║April    ║Diamond   ║Sweetpea          ║
║May      ║Emerald   ║Lily Of The Valley║
║June     ║Pearl     ║Rose              ║
║July     ║Ruby      ║Larkspur          ║
║August   ║Peridot   ║Gladiolus         ║
║September║Sapphire  ║Aster             ║
║October  ║Opal      ║Calendula         ║
║November ║Topaz     ║Chrysanthemum     ║
║December ║Turquoise ║Narcissus         ║
╚═════════╩══════════╩══════════════════╝

See the following documentation for more information:

Txtble(data=(), **kwargs)

Create a new Txtble object. The table's data may be passed to the constructor as an iterable of rows of values, where each row is either an iterable of cell values or a mapping from header names to cell values; otherwise, the data starts out empty. In either case, further data rows can be added via the append() and extend() methods.

**kwargs are used to configure the Txtble instance; see "Configuration Options" below.

tbl.append(row)
Add a new data row at the bottom of the table. row can be either an iterable of cell values or a mapping from header names to cell values.
tbl.extend(rows)
Add zero or more new data rows at the bottom of the table
tbl.show() or str(tbl)

Convert the Txtble instance to a string showing a plain text table. Table cells and filler values that are not already strings are converted by calling str() on them; the exceptions are None values, which are displayed according to the none_str option (see below). All tab characters are expanded to spaces before building the table. If any of the resulting strings have indeterminate width (i.e., if wcwidth.wcswidth() returns a negative number for any of them), an IndeterminateWidthError (a subclass of ValueError) is raised.

Note that the resulting string will likely contain one or more embedded newlines, but (outside of some very odd cases) it will not end with a newline. This means that you can do print(tbl) and there won't be a blank line added at the end.

These options can be set either as keywords passed to the Txtble constructor or as attributes on a Txtble instance:

tbl = Txtble(data, border=False)
# Same as:
tbl = Txtble(data)
tbl.border = False
align: Union[str, Sequence[str]] = ()

A sequence of alignment specifiers indicating how the contents of each column, in order, should be horizontally aligned. The alignment specifiers are 'l' (left alignment), 'c' (centered alignment), and 'r' (right alignment). align may optionally be set to a single alignment specifier to cause all columns to be aligned in that way.

An alignment specifier may optionally include 'n' to cause all numbers in the relevant column to be aligned on their decimal point; the 'l', 'c', or 'r' then determines how the "block" of numbers is aligned as a whole (This is generally only relevant if the column also contains a string value longer than any of the numbers). An alignment specifier of just 'n' is equivalent to 'ln' or 'nl'.

align_fill: str = 'l'
If there are more columns than there are entries in align, the extra columns will have their alignment set to align_fill.
border: Union[bool, BorderStyle] = True
Whether to draw a border around the edge of the table. border may optionally be set to a BorderStyle instance to set the characters used for drawing the border around the edge of the table. Individual edges can be toggled or stylized by setting the bottom_border, left_border, right_border, and top_border options.
border_style: BorderStyle = ASCII_BORDERS
A BorderStyle instance specifying the characters to use for drawing all of the table's borders & rules. The border style can be overridden for individual borders by setting their respective options (border, column_border, etc.) to BorderStyle instances. See "BorderStyle" below for more information.
bottom_border: Union[bool, BorderStyle, None] = None
Whether to draw a border along the bottom edge of the table. The default value of None means to inherit the value set for border. bottom_border may optionally be set to a BorderStyle instance to set the characters used for drawing the border along the bottom edge.
break_long_words: bool = True
Whether to force a line break in the middle of a word if said word is too long for the column's width
break_on_hyphens: bool = True
Whether to break on hyphens in addition to whitespace when wrapping text
column_border: Union[bool, BorderStyle] = True
Whether to draw a vertical rule between individual columns. column_border may optionally be set to a BorderStyle instance to set the characters used for drawing the vertical rules between columns.
columns: Optional[int] = None
An optional positive integer. When set, show exactly the given number of columns per row, adding cells with row_fill and discarding extra cells as needed. If headers is also set, its length must equal columns or else a ValueError is raised. Setting both columns and headers causes header_fill to be ignored.
dict_fill: Any
If a header name does not appear as a key in a dict/mapping row, the value of dict_fill will be used for the corresponding cell value. If dict_fill is not set, a missing key will cause a KeyError to be raised.
header_border: Union[bool, BorderStyle, None] = None

Whether to draw a horizontal rule above the data rows, below the header row (if any). The default value of None means that the border will be drawn if & only if headers is non-None. header_border may optionally be set to a BorderStyle instance to set the characters used for drawing the horizontal rule above the data rows.

If headers is None and top_border is set to a true value (or inherits a true value from border), the header border will not be drawn.

header_fill: Any = None
When headers is non-None and columns is None, this option determines how rows with more columns than there are headers are handled. When header_fill=None, any extra columns are discarded from long rows. For all other values, the header row will be extended to the length of the longest data row, and the new header cells will contain the header_fill value.
headers: Optional[list] = None

An optional list of cell values to display in a row at the top of the table. Setting this option also implicitly sets a minimum number of columns per row; see header_fill for allowing extra columns.

If headers is set to an empty list, header_fill must be set to a non-None value or else a ValueError will be raised upon trying to render the Txtble.

left_border: Union[bool, BorderStyle, None] = None
Whether to draw a border along the left edge of the table. The default value of None means to inherit the value set for border. left_border may optionally be set to a BorderStyle instance to set the characters used for drawing the border along the left edge.
left_padding: Union[int, str, None] = None
Padding to insert on the left of every table cell. This can be either an integer (to insert that many space characters) or a string. If a string, it may not contain any newlines. The default value of None means to inherit the value set for padding.
len_func: Optional[Callable[[str], int]]
The function to use for calculating how many terminal cells wide a string is; it should take one string argument and return a width. Returning a negative width causes Txtble to raise an IndeterminateWidthError. The default value (also used when set to None) is with_color_stripped(wcwidth.wcswidth) (See "Other" below).
none_str: Any = ''
The string to display in place of None values (Setting none_str=None is the same as setting it to 'None')
padding: Union[int, str] = 0
Padding to insert on the left & right of every table cell. This can be either an integer (to insert that many space characters) or a string. If a string, it may not contain any newlines. Padding for the left and right of table cells can be specified separately via the left_padding and right_padding options.
right_border: Union[bool, BorderStyle, None] = None
Whether to draw a border along the right edge of the table. The default value of None means to inherit the value set for border. right_border may optionally be set to a BorderStyle instance to set the characters used for drawing the border along the right edge.
right_padding: Union[int, str, None] = None
Padding to insert on the right of every table cell. This can be either an integer (to insert that many space characters) or a string. If a string, it may not contain any newlines. The default value of None means to inherit the value set for padding.
row_border: Union[bool, BorderStyle] = False
Whether to draw horizontal rules between data rows. row_border may optionally be set to a BorderStyle instance to set the characters used for drawing the horizontal rules between data rows.
row_fill: Any = ''
If the rows of a table differ in number of columns, cells are added to the shorter rows until they all line up, and the added cells contain row_fill as their value.
rstrip: bool = True
When border=False, setting rstrip=False will cause the last cell of each row to still be padded with trailing whitespace and padding in order to reach the full column width. (Normally, this whitespace and padding is omitted when border=False as there is no end-of-line border to align.) This option is useful if you wish to append text to one or more lines of the output and have it appear strictly outside the table.
top_border: Union[bool, BorderStyle, None] = None
Whether to draw a border along the top edge of the table. The default value of None means to inherit the value set for border. top_border may optionally be set to a BorderStyle instance to set the characters used for drawing the border along the top edge.
valign: Union[str, Sequence[str]] = ()
A sequence of vertical alignment specifiers indicating how the contents of each column, in order, should be vertically aligned. The vertical alignment specifiers are 't' (top alignment), 'm' (middle alignment), and 'b' (bottom alignment). valign may optionally be set to a single vertical alignment specifier to cause all columns to be vertically aligned in that way.
valign_fill: str = 't'
If there are more columns than there are entries in valign, the extra columns will have their vertical alignment set to valign_fill.
width_fill: Optional[int] = None
If there are more columns than there are entries in widths, the extra columns will have their widths set to width_fill.
widths: Union[int, Sequence[Optional[int]], None] = ()
A sequence of integers specifying the width of each column, in order. Lines wider than the given width will be wrapped; the wrapping can be configured via the break_long_words and break_on_hyphens options. A width of None disables wrapping for that column and causes the column's width to be set to the width of the longest line. widths may optionally be set to a single width to cause all columns to be that wide.
wrap_func: Optional[Callable[[str, int], Iterable[str]]] = None
The function to use for wrapping long lines; it should take a string and a width and return an iterable of strings. The default value of None causes a custom function to be used that properly handles fullwidth characters, ANSI color escape sequences, etc.; if your table contains such strings, any user-supplied wrap_func must be able to handle them as well. When wrap_func is set to a user-supplied value, the break_long_words and break_on_hyphens options are ignored.

The BorderStyle class is a namedtuple listing the strings to use for drawing a table's borders & rules. Its attributes are:

Attribute Description Example
hline horizontal line
vline vertical line
ulcorner upper-left box corner
urcorner upper-right box corner
llcorner lower-left box corner
lrcorner lower-right box corner
vrtee tee pointing right
vltee tee pointing left
dhtee tee pointing down
uhtee tee pointing up
plus cross/four-way joint

txtble provides the following predefined BorderStyle instances:

ASCII_BORDERS

The default border style. Draws borders using only the ASCII characters -, |, and +:

+-+-+
|A|B|
+-+-+
|C|D|
+-+-+
ASCII_EQ_BORDERS

Like ASCII_BORDERS, but uses = in place of -:

+=+=+
|A|B|
+=+=+
|C|D|
+=+=+
LIGHT_BORDERS

Uses the light box drawing characters:

┌─┬─┐
|A|B|
├─┼─┤
|C|D|
└─┴─┘
HEAVY_BORDERS

Uses the heavy box drawing characters:

┏━┳━┓
┃A┃B┃
┣━╋━┫
┃C┃D┃
┗━┻━┛
DOUBLE_BORDERS

Uses the double box drawing characters:

╔═╦═╗
║A║B║
╠═╬═╣
║C║D║
╚═╩═╝
DOT_BORDERS

Uses , , and ·:

·⋯·⋯·
⋮A⋮B⋮
·⋯·⋯·
⋮C⋮D⋮
·⋯·⋯·

If you define your own custom instances of BorderStyle, they must adhere to the following rules:

  • The hline string must be exactly one terminal column wide (the same width as a space character).
  • All strings other than hline must be the same width.
  • No string may contain a newline.
IndeterminateWidthError
Subclass of ValueError. Raised when a string is reported as having negative/indeterminate width. (For the default len_func, this happens when the string contains a DEL or a C0 or C1 control character other than a tab, newline, or ANSI color escape sequence.) The string in question is available as the exception's string attribute.
NumericWidthOverflowError
Subclass of ValueError. Raised when a column has a non-None width, the column's align value contains 'n', and aligning the numbers in the column along their decimal points would cause one or more cells to exceed the column's width.
UnterminatedColorError
Subclass of ValueError. Raised by with_color_stripped upon encountering an ANSI color escape sequence that is not eventually terminated by a reset/sgr0 sequence. The string in question is available as the exception's string attribute.
with_color_stripped
A function decorator for applying to len or imitators thereof that strips ANSI color sequences from a single string argument before passing it on. If any color sequences are not followed by a reset sequence, an UnterminatedColorError is raised.

txtble's People

Contributors

dependabot[bot] avatar jwodder avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

Forkers

pombredanne

txtble's Issues

Add a `VRule` class

Add a VRule class for specifying the format of vertical rules in tables.

  • VRule constructor arguments (all keyword-only; cf. HRule constructor arguments in #3):

    • fill: Optional[str] = None
    • joint: Optional[str] = None
    • top_cap: Optional[str] = None
    • bottom_cap: Optional[str] = None
    • border_style: Optional[BorderStyle] = None
  • VRules are passed to a Txtble as elements of a colsep list configuration option and/or as the value of a colsep_fill configuration option.

    • A string element s of colsep/value of colsep_fill is equivalent to VRule(fill=s, joint=s).
  • HRule (#3) and VRule should have priority: int parameters (defaulting to 0) for controlling what happens when an HRule and a VRule meet; the rule with the higher priority is used to draw the joint, and if they have the same priority, ???

  • When an HRule or VRule meets the table border, whose cap style is used?

Add a `Cell` class

Add a public Cell class (not to be confused with the Cell class used internally by the code as of v0.12.0, which will have to be renamed) that wraps and can be passed in place of a cell's content and that takes optional parameters for setting the alignment, padding, etc. for just that cell.

  • Cell(None) should be the same as "None", regardless of the value of none_str
  • Should Cells be allowed in headers? How would that interact with dict rows?

Some basic test cases for this feature can be found on the feature/cell-class branch.

Add `Txtble` subclasses with default values configured for various common table styles

  • Settings for a reStructuredText "simple table":

    RST_BORDER = BorderStyle(
        hline    = "=",
        vline    = "  ",
        ulcorner = "  ",
        urcorner = "  ",
        llcorner = "  ",
        lrcorner = "  ",
        vrtee    = "  ",
        vltee    = "  ",
        dhtee    = "  ",
        uhtee    = "  ",
        plus     = "  ",
    )
    
    border_style = RST_BORDER
    left_border  = False
    right_border = False

    Note that this does not support indicating column spans with hyphen rules. It also can result in multiline or blank cells in the first column, which rST will misinterpret.

  • Settings for a reStructuredText "grid table":

    padding = 1  # Optional
    row_border = True
    header_border = ASCII_EQ_BORDERS
  • Settings for a GitHub-Flavored Markdown table, sans alignment-indicating colons:

    GFM_LEFT_BORDER_STYLE = BorderStyle(
        hline = None,
        vline = "| ",
        ulcorner = "| ",
        urcorner = "| ",
        llcorner = "| ",
        lrcorner = "| ",
        vrtee = "| ",
        vltee = "| ",
        dhtee = "| ",
        uhtee = "| ",
        plus = "| ",
    )
    
    GFM_RIGHT_BORDER_STYLE = BorderStyle(
        hline = None,
        vline = " |",
        ulcorner = " |",
        urcorner = " |",
        llcorner = " |",
        lrcorner = " |",
        vrtee = " |",
        vltee = " |",
        dhtee = " |",
        uhtee = " |",
        plus = " |",
    )
    
    GFM_COLUMN_BORDER_STYLE = BorderStyle(
        hline = None,
        vline = " | ",
        ulcorner = " | ",
        urcorner = " | ",
        llcorner = " | ",
        lrcorner = " | ",
        vrtee = " | ",
        vltee = " | ",
        dhtee = " | ",
        uhtee = " | ",
        plus = " | ",
    )
    
    GFM_HEADER_BORDER_STYLE = BorderStyle(
        hline = "-",
        vline = None,
        ulcorner = None,
        urcorner = None,
        llcorner = None,
        lrcorner = None,
        vrtee = "| ",
        vltee = " |",
        dhtee = None,
        uhtee = None,
        plus = " | ",
    )
    
    top_border = False
    bottom_border = False
    left_border = GFM_LEFT_BORDER_STYLE
    right_border = GFM_RIGHT_BORDER_STYLE
    column_border = GFM_COLUMN_BORDER_STYLE
    header_border = GFM_HEADER_BORDER_STYLE
  • Settings for a Pandoc Markdown table (requires colsep from #4 or #22):

    border = False
    bottom_border = True
    colsep = "  "
  • Cf. tabulate's tablefmt

Add an `HRule` class

Add an "HRule" class that can be passed in place of a data row to indicate that a horizontal rule should be inserted there in the resulting table.

  • HRule constructor arguments (all keyword-only):

    • fill: Optional[str] = None
    • joint: Optional[str] = None
    • left_cap: Optional[str] = None
    • right_cap: Optional[str] = None
    • border_style: Optional[BorderStyle] = None
      • When specified, border_style is used to fill in the default values for the other parameters. When border_style is None:
        • If fill is None, border_style inherits from the Txtble's row_border (or from border_style if that is not a BorderStyle)
        • If fill is not None, ???
  • Give HRule options for (a) not applying the rule's style to the table border intersections at the ends and (b) not applying the rule's style at column intersections?

    • Somehow give header_border and row_border analogous options?
    • Allow header_border and row_border to be set to HRules?
  • Give HRule options for controlling what happens when adjacent to a top_border, header_border, row_border, or bottom_border (possibilities: *_border takes precedence, HRule takes precedence, draw both)

Some basic test cases for this feature can be found on the feature/hrule branch.

Support table footers

Alternatively, the same effect could mostly just be achieved by manually inserting an HRule from #3 before the "footer" row.

Support treating `align`, `valign`, and `widths` as mutable sequences even when initialized from strings

Additionally, even when they are initialized from a list, it should be possible to set indices out of range of the original list but in range for the table's number of columns.

  • Should it also be possible to index the properties by column header text?
  • Idea: If the final types of the options are publicly exposed, then align_fill etc. can be deprecated in favor of align=FillAlign(["r"], fill="l") (Class name a WIP)

Some basic test cases for this feature can be found on the feature/align-index branch.

Support cells spanning multiple rows/columns

Extend the Cell class from #5 to take colspan: int = 1 and rowspan: int = 1 parameters for producing cells that potentially span multiple columns and/or rows of a table.

  • A multicolumn/multirow cell is passed to a Txtble at the location of the cell's top-left corner. The other cells that are "covered up by"/"merged into" a multicolumn/multirow cell must be completely absent from the data array. For example, a four-column row in which the second cell is two cells wide would be written as ["Foo", Cell("Bar", colspan=2), "Baz"] ("Baz" is here the cell in the fourth column, not the third).

    • If all the cells in a row are "covered" by other cells, the row still has to be present as an empty sequence.
  • By default, the width of a multicolumn cell takes precedence over the widths of the columns it spans.

    • If the multicolumn cell is narrower than the sum of the widths of the columns it spans (plus column separators), the multicolumn cell is widened to fit.
    • If a multicolumn cell's width X is greater than the sum Y of the widths of the N columns it spans, the columns are widened to match by adding ceil((X - Y) / N) spaces to the first (X - Y) % N columns and floor((X - Y) / N) to the rest.
    • If restrict_width=True is set on a multicolumn Cell, the columns it spans will take precedence over it, width-wise.
      • If such a cell's natural width is greater than the columns it spans, its width will be reduced to match.
      • restrict_width has no effect on Cells that span only one column
  • A multicolumn/multirow cell's alignment, padding, etc. is based on that of its upper-left corner.

Some basic test cases for this feature can be found on the feature/span branch.

Handle strings of "indeterminate" width (including ANSI escape sequences)

wcwidth.wcswidth() will report the length of a string as -1 ("indeterminate") if it contains any C0 or C1 control characters (other than NUL) or DEL; in particular, it will return -1 for any strings containing ANSI escape sequences. Txtble should have a decent way to handle these strings.

  • Should Txtble raise an error upon encountering any strings of indeterminate width? Some ANSI escape sequences (like screen clearing & cursor repositioning) would just completely wreck the table output anyway.

  • I plan to eventually give Txtble a configuration option for setting the len()/wcswidth() function to use, which would allow users to supply their own solution to the ANSI string width problem, but is the problem common enough to warrant built-in support in the library?

  • Use blessed.Terminal.length()?

  • Use one of the solutions at https://stackoverflow.com/q/2186919/744178?

Add easier ways to configure the column separator

  • Add a colsep: str option for setting the column separator to a constant string (Cf. the option of the same name from #4)
    • Let it be a sequence of strings in order to support "doubling" of specific vrules
  • Add an option for setting the column separator only for non-hrule rows (i.e., the separator between adjacent cells) (Cf. #9)
  • Allow column_border, left_border, and right_border to be just a string? (since only one string of BorderStyle gets used anyway)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.