Git Product home page Git Product logo

python-tabulate's Introduction

python-tabulate

Pretty-print tabular data in Python, a library and a command-line utility.

The main use cases of the library are:

  • printing small tables without hassle: just one function call, formatting is guided by the data itself
  • authoring tabular data for lightweight plain-text markup: multiple output formats suitable for further editing or transformation
  • readable presentation of mixed textual and numeric data: smart column alignment, configurable number formatting, alignment by a decimal point

Installation

To install the Python library and the command line utility, run:

pip install tabulate

The command line utility will be installed as tabulate to bin on Linux (e.g. /usr/bin); or as tabulate.exe to Scripts in your Python installation on Windows (e.g. C:\Python39\Scripts\tabulate.exe).

You may consider installing the library only for the current user:

pip install tabulate --user

In this case the command line utility will be installed to ~/.local/bin/tabulate on Linux and to %APPDATA%\Python\Scripts\tabulate.exe on Windows.

To install just the library on Unix-like operating systems:

TABULATE_INSTALL=lib-only pip install tabulate

On Windows:

set TABULATE_INSTALL=lib-only
pip install tabulate

Build status

Build status Build status

Library usage

The module provides just one function, tabulate, which takes a list of lists or another tabular data type as the first argument, and outputs a nicely formatted plain-text table:

>>> from tabulate import tabulate

>>> table = [["Sun",696000,1989100000],["Earth",6371,5973.6],
...          ["Moon",1737,73.5],["Mars",3390,641.85]]
>>> print(tabulate(table))
-----  ------  -------------
Sun    696000     1.9891e+09
Earth    6371  5973.6
Moon     1737    73.5
Mars     3390   641.85
-----  ------  -------------

The following tabular data types are supported:

  • list of lists or another iterable of iterables
  • list or another iterable of dicts (keys as columns)
  • dict of iterables (keys as columns)
  • list of dataclasses (Python 3.7+ only, field names as columns)
  • two-dimensional NumPy array
  • NumPy record arrays (names as columns)
  • pandas.DataFrame

Tabulate is a Python3 library.

Headers

The second optional argument named headers defines a list of column headers to be used:

>>> print(tabulate(table, headers=["Planet","R (km)", "mass (x 10^29 kg)"]))
Planet      R (km)    mass (x 10^29 kg)
--------  --------  -------------------
Sun         696000           1.9891e+09
Earth         6371        5973.6
Moon          1737          73.5
Mars          3390         641.85

If headers="firstrow", then the first row of data is used:

>>> print(tabulate([["Name","Age"],["Alice",24],["Bob",19]],
...                headers="firstrow"))
Name      Age
------  -----
Alice      24
Bob        19

If headers="keys", then the keys of a dictionary/dataframe, or column indices are used. It also works for NumPy record arrays and lists of dictionaries or named tuples:

>>> print(tabulate({"Name": ["Alice", "Bob"],
...                 "Age": [24, 19]}, headers="keys"))
  Age  Name
-----  ------
   24  Alice
   19  Bob

Row Indices

By default, only pandas.DataFrame tables have an additional column called row index. To add a similar column to any other type of table, pass showindex="always" or showindex=True argument to tabulate(). To suppress row indices for all types of data, pass showindex="never" or showindex=False. To add a custom row index column, pass showindex=rowIDs, where rowIDs is some iterable:

>>> print(tabulate([["F",24],["M",19]], showindex="always"))
-  -  --
0  F  24
1  M  19
-  -  --

Table format

There is more than one way to format a table in plain text. The third optional argument named tablefmt defines how the table is formatted.

Supported table formats are:

  • "plain"
  • "simple"
  • "github"
  • "grid"
  • "simple_grid"
  • "rounded_grid"
  • "heavy_grid"
  • "mixed_grid"
  • "double_grid"
  • "fancy_grid"
  • "outline"
  • "simple_outline"
  • "rounded_outline"
  • "heavy_outline"
  • "mixed_outline"
  • "double_outline"
  • "fancy_outline"
  • "pipe"
  • "orgtbl"
  • "asciidoc"
  • "jira"
  • "presto"
  • "pretty"
  • "psql"
  • "rst"
  • "mediawiki"
  • "moinmoin"
  • "youtrack"
  • "html"
  • "unsafehtml"
  • "latex"
  • "latex_raw"
  • "latex_booktabs"
  • "latex_longtable"
  • "textile"
  • "tsv"

plain tables do not use any pseudo-graphics to draw lines:

>>> table = [["spam",42],["eggs",451],["bacon",0]]
>>> headers = ["item", "qty"]
>>> print(tabulate(table, headers, tablefmt="plain"))
item      qty
spam       42
eggs      451
bacon       0

simple is the default format (the default may change in future versions). It corresponds to simple_tables in Pandoc Markdown extensions:

>>> print(tabulate(table, headers, tablefmt="simple"))
item      qty
------  -----
spam       42
eggs      451
bacon       0

github follows the conventions of GitHub flavored Markdown. It corresponds to the pipe format without alignment colons:

>>> print(tabulate(table, headers, tablefmt="github"))
| item   | qty   |
|--------|-------|
| spam   | 42    |
| eggs   | 451   |
| bacon  | 0     |

grid is like tables formatted by Emacs' table.el package. It corresponds to grid_tables in Pandoc Markdown extensions:

>>> print(tabulate(table, headers, tablefmt="grid"))
+--------+-------+
| item   |   qty |
+========+=======+
| spam   |    42 |
+--------+-------+
| eggs   |   451 |
+--------+-------+
| bacon  |     0 |
+--------+-------+

simple_grid draws a grid using single-line box-drawing characters:

>>> print(tabulate(table, headers, tablefmt="simple_grid"))
┌────────┬───────┐
│ item   │   qty │
├────────┼───────┤
│ spam   │    42 │
├────────┼───────┤
│ eggs   │   451 │
├────────┼───────┤
│ bacon  │     0 │
└────────┴───────┘

rounded_grid draws a grid using single-line box-drawing characters with rounded corners:

>>> print(tabulate(table, headers, tablefmt="rounded_grid"))
╭────────┬───────╮
│ item   │   qty │
├────────┼───────┤
│ spam   │    42 │
├────────┼───────┤
│ eggs   │   451 │
├────────┼───────┤
│ bacon  │     0 │
╰────────┴───────╯

heavy_grid draws a grid using bold (thick) single-line box-drawing characters:

>>> print(tabulate(table, headers, tablefmt="heavy_grid"))
┏━━━━━━━━┳━━━━━━━┓
┃ item   ┃   qty ┃
┣━━━━━━━━╋━━━━━━━┫
┃ spam   ┃    42 ┃
┣━━━━━━━━╋━━━━━━━┫
┃ eggs   ┃   451 ┃
┣━━━━━━━━╋━━━━━━━┫
┃ bacon  ┃     0 ┃
┗━━━━━━━━┻━━━━━━━┛

mixed_grid draws a grid using a mix of light (thin) and heavy (thick) lines box-drawing characters:

>>> print(tabulate(table, headers, tablefmt="mixed_grid"))
┍━━━━━━━━┯━━━━━━━┑
│ item   │   qty │
┝━━━━━━━━┿━━━━━━━┥
│ spam   │    42 │
├────────┼───────┤
│ eggs   │   451 │
├────────┼───────┤
│ bacon  │     0 │
┕━━━━━━━━┷━━━━━━━┙

double_grid draws a grid using double-line box-drawing characters:

>>> print(tabulate(table, headers, tablefmt="double_grid"))
╔════════╦═══════╗
║ item   ║   qty ║
╠════════╬═══════╣
║ spam   ║    42 ║
╠════════╬═══════╣
║ eggs   ║   451 ║
╠════════╬═══════╣
║ bacon  ║     0 ║
╚════════╩═══════╝

fancy_grid draws a grid using a mix of single and double-line box-drawing characters:

>>> print(tabulate(table, headers, tablefmt="fancy_grid"))
╒════════╤═══════╕
│ item   │   qty │
╞════════╪═══════╡
│ spam   │    42 │
├────────┼───────┤
│ eggs   │   451 │
├────────┼───────┤
│ bacon  │     0 │
╘════════╧═══════╛

outline is the same as the grid format but doesn't draw lines between rows:

>>> print(tabulate(table, headers, tablefmt="outline"))
+--------+-------+
| item   |   qty |
+========+=======+
| spam   |    42 |
| eggs   |   451 |
| bacon  |     0 |
+--------+-------+

simple_outline is the same as the simple_grid format but doesn't draw lines between rows:

>>> print(tabulate(table, headers, tablefmt="simple_outline"))
┌────────┬───────┐
│ item   │   qty │
├────────┼───────┤
│ spam   │    42 │
│ eggs   │   451 │
│ bacon  │     0 │
└────────┴───────┘

rounded_outline is the same as the rounded_grid format but doesn't draw lines between rows:

>>> print(tabulate(table, headers, tablefmt="rounded_outline"))
╭────────┬───────╮
│ item   │   qty │
├────────┼───────┤
│ spam   │    42 │
│ eggs   │   451 │
│ bacon  │     0 │
╰────────┴───────╯

heavy_outline is the same as the heavy_grid format but doesn't draw lines between rows:

>>> print(tabulate(table, headers, tablefmt="heavy_outline"))
┏━━━━━━━━┳━━━━━━━┓
┃ item   ┃   qty ┃
┣━━━━━━━━╋━━━━━━━┫
┃ spam   ┃    42 ┃
┃ eggs   ┃   451 ┃
┃ bacon  ┃     0 ┃
┗━━━━━━━━┻━━━━━━━┛

mixed_outline is the same as the mixed_grid format but doesn't draw lines between rows:

>>> print(tabulate(table, headers, tablefmt="mixed_outline"))
┍━━━━━━━━┯━━━━━━━┑
│ item   │   qty │
┝━━━━━━━━┿━━━━━━━┥
│ spam   │    42 │
│ eggs   │   451 │
│ bacon  │     0 │
┕━━━━━━━━┷━━━━━━━┙

double_outline is the same as the double_grid format but doesn't draw lines between rows:

>>> print(tabulate(table, headers, tablefmt="double_outline"))
╔════════╦═══════╗
║ item   ║   qty ║
╠════════╬═══════╣
║ spam   ║    42 ║
║ eggs   ║   451 ║
║ bacon  ║     0 ║
╚════════╩═══════╝

fancy_outline is the same as the fancy_grid format but doesn't draw lines between rows:

>>> print(tabulate(table, headers, tablefmt="fancy_outline"))
╒════════╤═══════╕
│ item   │   qty │
╞════════╪═══════╡
│ spam   │    42 │
│ eggs   │   451 │
│ bacon  │     0 │
╘════════╧═══════╛

presto is like tables formatted by Presto cli:

>>> print(tabulate(table, headers, tablefmt="presto"))
 item   |   qty
--------+-------
 spam   |    42
 eggs   |   451
 bacon  |     0

pretty attempts to be close to the format emitted by the PrettyTables library:

>>> print(tabulate(table, headers, tablefmt="pretty"))
+-------+-----+
| item  | qty |
+-------+-----+
| spam  | 42  |
| eggs  | 451 |
| bacon |  0  |
+-------+-----+

psql is like tables formatted by Postgres' psql cli:

>>> print(tabulate(table, headers, tablefmt="psql"))
+--------+-------+
| item   |   qty |
|--------+-------|
| spam   |    42 |
| eggs   |   451 |
| bacon  |     0 |
+--------+-------+

pipe follows the conventions of PHP Markdown Extra extension. It corresponds to pipe_tables in Pandoc. This format uses colons to indicate column alignment:

>>> print(tabulate(table, headers, tablefmt="pipe"))
| item   |   qty |
|:-------|------:|
| spam   |    42 |
| eggs   |   451 |
| bacon  |     0 |

asciidoc formats data like a simple table of the AsciiDoctor format:

>>> print(tabulate(table, headers, tablefmt="asciidoc"))
[cols="8<,7>",options="header"]
|====
| item   |   qty
| spam   |    42
| eggs   |   451
| bacon  |     0
|====

orgtbl follows the conventions of Emacs org-mode, and is editable also in the minor orgtbl-mode. Hence its name:

>>> print(tabulate(table, headers, tablefmt="orgtbl"))
| item   |   qty |
|--------+-------|
| spam   |    42 |
| eggs   |   451 |
| bacon  |     0 |

jira follows the conventions of Atlassian Jira markup language:

>>> print(tabulate(table, headers, tablefmt="jira"))
|| item   ||   qty ||
| spam   |    42 |
| eggs   |   451 |
| bacon  |     0 |

rst formats data like a simple table of the reStructuredText format:

>>> print(tabulate(table, headers, tablefmt="rst"))
======  =====
item      qty
======  =====
spam       42
eggs      451
bacon       0
======  =====

mediawiki format produces a table markup used in Wikipedia and on other MediaWiki-based sites:

>>> print(tabulate(table, headers, tablefmt="mediawiki"))
{| class="wikitable" style="text-align: left;"
|+ <!-- caption -->
|-
! item   !! style="text-align: right;"|   qty
|-
| spam   || style="text-align: right;"|    42
|-
| eggs   || style="text-align: right;"|   451
|-
| bacon  || style="text-align: right;"|     0
|}

moinmoin format produces a table markup used in MoinMoin wikis:

>>> print(tabulate(table, headers, tablefmt="moinmoin"))
|| ''' item   ''' || ''' quantity   ''' ||
||  spam    ||  41.999      ||
||  eggs    ||  451         ||
||  bacon   ||              ||

youtrack format produces a table markup used in Youtrack tickets:

>>> print(tabulate(table, headers, tablefmt="youtrack"))
||  item    ||  quantity   ||
|   spam    |  41.999      |
|   eggs    |  451         |
|   bacon   |              |

textile format produces a table markup used in Textile format:

>>> print(tabulate(table, headers, tablefmt="textile"))
|_.  item   |_.   qty |
|<. spam    |>.    42 |
|<. eggs    |>.   451 |
|<. bacon   |>.     0 |

html produces standard HTML markup as an html.escape'd str with a .repr_html method so that Jupyter Lab and Notebook display the HTML and a .str property so that the raw HTML remains accessible. unsafehtml table format can be used if an unescaped HTML is required:

>>> print(tabulate(table, headers, tablefmt="html"))
<table>
<tbody>
<tr><th>item  </th><th style="text-align: right;">  qty</th></tr>
<tr><td>spam  </td><td style="text-align: right;">   42</td></tr>
<tr><td>eggs  </td><td style="text-align: right;">  451</td></tr>
<tr><td>bacon </td><td style="text-align: right;">    0</td></tr>
</tbody>
</table>

latex format creates a tabular environment for LaTeX markup, replacing special characters like _ or \ to their LaTeX correspondents:

>>> print(tabulate(table, headers, tablefmt="latex"))
\begin{tabular}{lr}
\hline
 item   &   qty \\
\hline
 spam   &    42 \\
 eggs   &   451 \\
 bacon  &     0 \\
\hline
\end{tabular}

latex_raw behaves like latex but does not escape LaTeX commands and special characters.

latex_booktabs creates a tabular environment for LaTeX markup using spacing and style from the booktabs package.

latex_longtable creates a table that can stretch along multiple pages, using the longtable package.

Column alignment

tabulate is smart about column alignment. It detects columns which contain only numbers, and aligns them by a decimal point (or flushes them to the right if they appear to be integers). Text columns are flushed to the left.

You can override the default alignment with numalign and stralign named arguments. Possible column alignments are: right, center, left, decimal (only for numbers), and None (to disable alignment).

Aligning by a decimal point works best when you need to compare numbers at a glance:

>>> print(tabulate([[1.2345],[123.45],[12.345],[12345],[1234.5]]))
----------
    1.2345
  123.45
   12.345
12345
 1234.5
----------

Compare this with a more common right alignment:

>>> print(tabulate([[1.2345],[123.45],[12.345],[12345],[1234.5]], numalign="right"))
------
1.2345
123.45
12.345
 12345
1234.5
------

For tabulate, anything which can be parsed as a number is a number. Even numbers represented as strings are aligned properly. This feature comes in handy when reading a mixed table of text and numbers from a file:

>>> import csv ; from StringIO import StringIO
>>> table = list(csv.reader(StringIO("spam, 42\neggs, 451\n")))
>>> table
[['spam', ' 42'], ['eggs', ' 451']]
>>> print(tabulate(table))
----  ----
spam    42
eggs   451
----  ----

To disable this feature use disable_numparse=True.

>>> print(tabulate.tabulate([["Ver1", "18.0"], ["Ver2","19.2"]], tablefmt="simple", disable_numparse=True))
----  ----
Ver1  18.0
Ver2  19.2
----  ----

Custom column alignment

tabulate allows a custom column alignment to override the smart alignment described above. Use colglobalalign to define a global setting. Possible alignments are: right, center, left, decimal (only for numbers). Furthermore, you can define colalign for column-specific alignment as a list or a tuple. Possible values are global (keeps global setting), right, center, left, decimal (only for numbers), None (to disable alignment). Missing alignments are treated as global.

>>> print(tabulate([[1,2,3,4],[111,222,333,444]], colglobalalign='center', colalign = ('global','left','right')))
---  ---  ---  ---
 1   2      3   4
111  222  333  444
---  ---  ---  ---

Custom header alignment

Headers' alignment can be defined separately from columns'. Like for columns, you can use:

  • headersglobalalign to define a header-specific global alignment setting. Possible values are right, center, left, None (to follow column alignment),
  • headersalign list or tuple to further specify header-wise alignment. Possible values are global (keeps global setting), same (follow column alignment), right, center, left, None (to disable alignment). Missing alignments are treated as global.
>>> print(tabulate([[1,2,3,4,5,6],[111,222,333,444,555,666]], colglobalalign = 'center', colalign = ('left',), headers = ['h','e','a','d','e','r'], headersglobalalign = 'right', headersalign = ('same','same','left','global','center')))

h     e   a      d   e     r
---  ---  ---  ---  ---  ---
1     2    3    4    5    6
111  222  333  444  555  666

Number formatting

tabulate allows to define custom number formatting applied to all columns of decimal numbers. Use floatfmt named argument:

>>> print(tabulate([["pi",3.141593],["e",2.718282]], floatfmt=".4f"))
--  ------
pi  3.1416
e   2.7183
--  ------

floatfmt argument can be a list or a tuple of format strings, one per column, in which case every column may have different number formatting:

>>> print(tabulate([[0.12345, 0.12345, 0.12345]], floatfmt=(".1f", ".3f")))
---  -----  -------
0.1  0.123  0.12345
---  -----  -------

intfmt works similarly for integers

>>> print(tabulate([["a",1000],["b",90000]], intfmt=","))
-  ------
a   1,000
b  90,000
-  ------

Text formatting

By default, tabulate removes leading and trailing whitespace from text columns. To disable whitespace removal, set the global module-level flag PRESERVE_WHITESPACE:

import tabulate
tabulate.PRESERVE_WHITESPACE = True

Wide (fullwidth CJK) symbols

To properly align tables which contain wide characters (typically fullwidth glyphs from Chinese, Japanese or Korean languages), the user should install wcwidth library. To install it together with tabulate:

pip install tabulate[widechars]

Wide character support is enabled automatically if wcwidth library is already installed. To disable wide characters support without uninstalling wcwidth, set the global module-level flag WIDE_CHARS_MODE:

import tabulate
tabulate.WIDE_CHARS_MODE = False

Multiline cells

Most table formats support multiline cell text (text containing newline characters). The newline characters are honored as line break characters.

Multiline cells are supported for data rows and for header rows.

Further automatic line breaks are not inserted. Of course, some output formats such as latex or html handle automatic formatting of the cell content on their own, but for those that don't, the newline characters in the input cell text are the only means to break a line in cell text.

Note that some output formats (e.g. simple, or plain) do not represent row delimiters, so that the representation of multiline cells in such formats may be ambiguous to the reader.

The following examples of formatted output use the following table with a multiline cell, and headers with a multiline cell:

>>> table = [["eggs",451],["more\nspam",42]]
>>> headers = ["item\nname", "qty"]

plain tables:

>>> print(tabulate(table, headers, tablefmt="plain"))
item      qty
name
eggs      451
more       42
spam

simple tables:

>>> print(tabulate(table, headers, tablefmt="simple"))
item      qty
name
------  -----
eggs      451
more       42
spam

grid tables:

>>> print(tabulate(table, headers, tablefmt="grid"))
+--------+-------+
| item   |   qty |
| name   |       |
+========+=======+
| eggs   |   451 |
+--------+-------+
| more   |    42 |
| spam   |       |
+--------+-------+

fancy_grid tables:

>>> print(tabulate(table, headers, tablefmt="fancy_grid"))
╒════════╤═══════╕
│ item   │   qty │
│ name   │       │
╞════════╪═══════╡
│ eggs   │   451 │
├────────┼───────┤
│ more   │    42 │
│ spam   │       │
╘════════╧═══════╛

pipe tables:

>>> print(tabulate(table, headers, tablefmt="pipe"))
| item   |   qty |
| name   |       |
|:-------|------:|
| eggs   |   451 |
| more   |    42 |
| spam   |       |

orgtbl tables:

>>> print(tabulate(table, headers, tablefmt="orgtbl"))
| item   |   qty |
| name   |       |
|--------+-------|
| eggs   |   451 |
| more   |    42 |
| spam   |       |

jira tables:

>>> print(tabulate(table, headers, tablefmt="jira"))
| item   |   qty |
| name   |       |
|:-------|------:|
| eggs   |   451 |
| more   |    42 |
| spam   |       |

presto tables:

>>> print(tabulate(table, headers, tablefmt="presto"))
 item   |   qty
 name   |
--------+-------
 eggs   |   451
 more   |    42
 spam   |

pretty tables:

>>> print(tabulate(table, headers, tablefmt="pretty"))
+------+-----+
| item | qty |
| name |     |
+------+-----+
| eggs | 451 |
| more | 42  |
| spam |     |
+------+-----+

psql tables:

>>> print(tabulate(table, headers, tablefmt="psql"))
+--------+-------+
| item   |   qty |
| name   |       |
|--------+-------|
| eggs   |   451 |
| more   |    42 |
| spam   |       |
+--------+-------+

rst tables:

>>> print(tabulate(table, headers, tablefmt="rst"))
======  =====
item      qty
name
======  =====
eggs      451
more       42
spam
======  =====

Multiline cells are not well-supported for the other table formats.

Automating Multilines

While tabulate supports data passed in with multilines entries explicitly provided, it also provides some support to help manage this work internally.

The maxcolwidths argument is a list where each entry specifies the max width for it's respective column. Any cell that will exceed this will automatically wrap the content. To assign the same max width for all columns, a singular int scaler can be used.

Use None for any columns where an explicit maximum does not need to be provided, and thus no automate multiline wrapping will take place.

The wrapping uses the python standard textwrap.wrap function with default parameters - aside from width.

This example demonstrates usage of automatic multiline wrapping, though typically the lines being wrapped would probably be significantly longer than this.

>>> print(tabulate([["John Smith", "Middle Manager"]], headers=["Name", "Title"], tablefmt="grid", maxcolwidths=[None, 8]))
+------------+---------+
| Name       | Title   |
+============+=========+
| John Smith | Middle  |
|            | Manager |
+------------+---------+

Adding Separating lines

One might want to add one or more separating lines to highlight different sections in a table.

The separating lines will be of the same type as the one defined by the specified formatter as either the linebetweenrows, linebelowheader, linebelow, lineabove or just a simple empty line when none is defined for the formatter

>>> from tabulate import tabulate, SEPARATING_LINE

table = [["Earth",6371],
         ["Mars",3390],
         SEPARATING_LINE,
         ["Moon",1737]]
print(tabulate(table, tablefmt="simple"))
-----  ----
Earth  6371
Mars   3390
-----  ----
Moon   1737
-----  ----

ANSI support

ANSI escape codes are non-printable byte sequences usually used for terminal operations like setting color output or modifying cursor positions. Because multi-byte ANSI sequences are inherently non-printable, they can still introduce unwanted extra length to strings. For example:

>>> len('\033[31mthis text is red\033[0m')  # printable length is 16
25

To deal with this, string lengths are calculated after first removing all ANSI escape sequences. This ensures that the actual printable length is used for column widths, rather than the byte length. In the final, printable table, however, ANSI escape sequences are not removed so the original styling is preserved.

Some terminals support a special grouping of ANSI escape sequences that are intended to display hyperlinks much in the same way they are shown in browsers. These are handled just as mentioned before: non-printable ANSI escape sequences are removed prior to string length calculation. The only diifference with escaped hyperlinks is that column width will be based on the length of the URL text rather than the URL itself (terminals would show this text). For example:

>>> len('\x1b]8;;https://example.com\x1b\\example\x1b]8;;\x1b\\')  # display length is 7, showing 'example'
45

Usage of the command line utility

Usage: tabulate [options] [FILE ...]

FILE                      a filename of the file with tabular data;
                          if "-" or missing, read data from stdin.

Options:

-h, --help                show this message
-1, --header              use the first row of data as a table header
-o FILE, --output FILE    print table to FILE (default: stdout)
-s REGEXP, --sep REGEXP   use a custom column separator (default: whitespace)
-F FPFMT, --float FPFMT   floating point number format (default: g)
-I INTFMT, --int INTFMT   integer point number format (default: "")
-f FMT, --format FMT      set output table format; supported formats:
                          plain, simple, github, grid, fancy_grid, pipe,
                          orgtbl, rst, mediawiki, html, latex, latex_raw,
                          latex_booktabs, latex_longtable, tsv
                          (default: simple)

Performance considerations

Such features as decimal point alignment and trying to parse everything as a number imply that tabulate:

  • has to "guess" how to print a particular tabular data type
  • needs to keep the entire table in-memory
  • has to "transpose" the table twice
  • does much more work than it may appear

It may not be suitable for serializing really big tables (but who's going to do that, anyway?) or printing tables in performance sensitive applications. tabulate is about two orders of magnitude slower than simply joining lists of values with a tab, comma, or other separator.

At the same time, tabulate is comparable to other table pretty-printers. Given a 10x10 table (a list of lists) of mixed text and numeric data, tabulate appears to be slower than asciitable, and faster than PrettyTable and texttable The following mini-benchmark was run in Python 3.9.13 on Windows 10:

=================================  ==========  ===========
Table formatter                      time, μs    rel. time
=================================  ==========  ===========
csv to StringIO                          12.5          1.0
join with tabs and newlines              14.6          1.2
asciitable (0.8.0)                      192.0         15.4
tabulate (0.9.0)                        483.5         38.7
tabulate (0.9.0, WIDE_CHARS_MODE)       637.6         51.1
PrettyTable (3.4.1)                    1080.6         86.6
texttable (1.6.4)                      1390.3        111.4
=================================  ==========  ===========

Version history

The full version history can be found at the changelog.

How to contribute

Contributions should include tests and an explanation for the changes they propose. Documentation (examples, docstrings, README.md) should be updated accordingly.

This project uses pytest testing framework and tox to automate testing in different environments. Add tests to one of the files in the test/ folder.

To run tests on all supported Python versions, make sure all Python interpreters, pytest and tox are installed, then run tox in the root of the project source tree.

On Linux tox expects to find executables like python3.7, python3.8 etc. On Windows it looks for C:\Python37\python.exe, C:\Python38\python.exe etc. respectively.

One way to install all the required versions of the Python interpreter is to use pyenv. All versions can then be easily installed with something like:

 pyenv install 3.7.12
 pyenv install 3.8.12
 ...

Don't forget to change your PATH so that tox knows how to find all the installed versions. Something like

 export PATH="${PATH}:${HOME}/.pyenv/shims"

To test only some Python environments, use -e option. For example, to test only against Python 3.7 and Python 3.10, run:

tox -e py37,py310

in the root of the project source tree.

To enable NumPy and Pandas tests, run:

tox -e py37-extra,py310-extra

(this may take a long time the first time, because NumPy and Pandas will have to be installed in the new virtual environments)

To fix code formatting:

tox -e lint

See tox.ini file to learn how to use to test individual Python versions.

Contributors

Sergey Astanin, Pau Tallada Crespí, Erwin Marsi, Mik Kocikowski, Bill Ryder, Zach Dwiel, Frederik Rietdijk, Philipp Bogensberger, Greg (anonymous), Stefan Tatschner, Emiel van Miltenburg, Brandon Bennett, Amjith Ramanujam, Jan Schulz, Simon Percivall, Javier Santacruz López-Cepero, Sam Denton, Alexey Ziyangirov, acaird, Cesar Sanchez, naught101, John Vandenberg, Zack Dever, Christian Clauss, Benjamin Maier, Andy MacKinlay, Thomas Roten, Jue Wang, Joe King, Samuel Phan, Nick Satterly, Daniel Robbins, Dmitry B, Lars Butler, Andreas Maier, Dick Marinus, Sébastien Celles, Yago González, Andrew Gaul, Wim Glenn, Jean Michel Rouly, Tim Gates, John Vandenberg, Sorin Sbarnea, Wes Turner, Andrew Tija, Marco Gorelli, Sean McGinnis, danja100, endolith, Dominic Davis-Foster, pavlocat, Daniel Aslau, paulc, Felix Yan, Shane Loretz, Frank Busse, Harsh Singh, Derek Weitzel, Vladimir Vrzić, 서승우 (chrd5273), Georgy Frolov, Christian Cwienk, Bart Broere, Vilhelm Prytz, Alexander Gažo, Hugo van Kemenade, jamescooke, Matt Warner, Jérôme Provensal, Kevin Deldycke, Kian-Meng Ang, Kevin Patterson, Shodhan Save, cleoold, KOLANICH, Vijaya Krishna Kasula, Furcy Pin, Christian Fibich, Shaun Duncan, Dimitri Papadopoulos, Élie Goudout.

python-tabulate's People

Contributors

admackin avatar alexandergazo avatar arbor-acaird avatar astanin avatar bartbroere avatar benmaier avatar bryder avatar c-ezra-m avatar danielrobbins avatar dwiel avatar eliegoudout avatar emsrc avatar hugovk avatar kdeldycke avatar kolanich avatar larsbutler avatar magelisk avatar meeuw avatar naught101 avatar oxitnik avatar purplesword avatar sa-flxc avatar satterly avatar scls19fr avatar sloretz avatar tsroten avatar vrza avatar westurner avatar wimglenn avatar yagogg avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

python-tabulate's Issues

Feature Request: Transpose

Sometimes it would be great to transpose the table to fit it into a certains width. This happens particularly often when plotting multiple time series (e.g. with pyplot.plot(...)). In that case, being able to transpose the table would be a helpful addition.

In its basic form, this is a one-liner: data = zip(*data)
However, it becomes more verbose when considering headers, etc. and would just be a convience feature. Presumably, the debat is whether this should be done before, outside tabulate.
The change is minimal, let me know if you would like to have a pull request.

HTML format with grid

The existing "html" tablefmt produces good html tables but the are missing a border:

image

Maybe a good idea will be to add a "grid_html" tablefmt to produce the exact same table but with borders:
image

Support for dataclasses

As a wishlist item: it would be pretty cool if tabulate rows could be dataclass instances. This can already be simulated with dataclasses.asdict, but first-class support would be cool (and maybe enable some advanced features?)

Row titles when printing a dict of dicts

Is there a way to handle row titles nicely? E.g. not including them into data.

My use case, I have benchmark results looks like:

results = {
    'add': {'total': 10.132, 'in': 0.1234321, "out": 0.2343, "sleep": 0.05},
    'add-2': {'total': 3.132, 'in': 0.1234321, "out": 0.2343, "sleep": 0.05},
    'push': {'total': 323.132, 'in': 0.1234321, "out": 0.2343, "sleep": 0.05},
    'push-noop': {'total': 23.132, 'in': 0.1234321, "out": 0.2343, "sleep": 0.05},
}

And I want to show table with add, add-2, ... as row titles and total, in, ... as column headers.

Feature Request: Parser for tabulated data

Although it isn't actually part of the remit of the program, it would be amazing if there was some way of parsing data FROM tabulated markdown format back into a list. Surely it's as simple as writing "reverse" on a command somewhere? ;P

[REQ] Modifiers (such as ANSI escape) that can change per row as part of Data rows

I'm looking for something to print pretty tables for me for a project. tabulate seems to do 95% of what I want, with the exception that I want to change the background colours for some rows. Mostly, I want to alternate between white, and light-grey backgrounds, but I'd also like the ability to highlight certain rows, and with this in mind, maybe my ideal is to be able to add a function to the DataRow constructor, get_colour, such that for each row of data, get_colour(row) is run and the returned value is used to colour the row.

The actual colouring is doable with ANSI escape codes, I've already got them running for bold/underlines/foreground/backgrounds, but I have to treat each data row identically.

The way I see this working is the DataRow constructor has the optional get_colour parameter. If present, and a function, it is called on each row in turn, and the result is used to colour the row.
If one value is returned, use it to choose the colour of the font. If two values are returned (in a list), the first (if not None) is the foreground, and the second (if not None) is the background.

Each TableFormat would also have to have a "colour_encode" function. For instance, for ANSI escape codes, there'd first have to be a colour lookup (string name to integer), and then it would return \033]38;5;X or similar. There'd also need to be a "end colour" method, to reset colours. For HTML, the equivalent might be a closing tag.

I don't know if this is something this project wants. It's a lot of extra features and code for something that very few people (maybe only me) would use. If the project thinks it might be interesting, I'll happily write something, but otherwise I might just use the parts of tabulate that I want directly in my project.

Does this sound useful to others?

Feature request: Set max list length when using fancy_grid

Is there any way to set a max number of items before the script draws a second table to the right of the first one?

If I have say, 50 items which are being listed in fancy_grid mode, ideally I want to display only 15 to 20 before my terminal runs out of vertical space.
So being able to give a parameter like "max_list_length=20" would be a nice option to have.

Thank you!

Header order

Header order defined by the order that keys are discovered, rather than the order that they are present in.

E.g.

from collections import OrderedDict
from tabulate import tabulate

test_dict = [
        OrderedDict([("a", 1), ("b", 2), ("e", 5)]),
        OrderedDict([("a", 1), ("b", 2), ("c", 3), ("d", 4), ("e", 5)]),
    ]

print(tabulate(
    test_dict, headers="keys", tablefmt="simple", missingval="0"
))

gives the output:

  a    b    e    c    d
---  ---  ---  ---  ---
  1    2    5    0    0
  1    2    5    3    4

whereas I would prefer it in this order:

  a    b    c    d    e
---  ---  ---  ---  ---
  1    2    0    0    5
  1    2    3    4    5

Seems a bug for the layout for the Chinese Character

With Chinese characters in the DataFrame, it seems the tabulate package cannot identify the correct width.

import pandas as pd
import tabulate

print(tabulate.__version__)
print(tabulate.WIDE_CHARS_MODE)
df = pd.DataFrame({
    '序号':['001', '021', '032'],
    '类型': ['汽车TESLA', '飞机737-MAX', 'TRAIN'],
    '备注': ['特殊', '一长串字符', '字符+1020']
})
print(tabulate.tabulate(df, tablefmt="github"))

The Output is :
0.8.7
True
|---|-----|-------------|------------|
| 0 | 001 | 汽车TESLA | 特殊 |
| 1 | 021 | 飞机737-MAX | 一长串字符 |
| 2 | 032 | TRAIN | 字符+1020 |
Screen Shot 2020-04-21 at 12 58 57 AM

Feature Request: CSV

tabulate already supports a wide variety of output formats. Currently, the very basic CSV format is not one of them. I think it would be a great addition, as it is a standard format to copy-paste data into tools like matlab, excel, ...
Let me know if this feature is desired, then I will add it and open a pull request.

Feature request: Keep trailing zeros (or disable treating columns as numbers)

It would be nice if there was a switch to overwrite tabulate's smart processing of numbers. I am printing table with several columns that might contain strings that look like numbers (e.g. 8.10). If one of the rows contains a letter in such column, all is good, because tabulate assumes the whole column should be treated as a text. But when my data set does not contain a letter in that column, the strings get treated as numbers and trailing zeroes are automatically removed, which is bad.

Multi-line cells with github formatting

The multi-line formatting for github tables doesn't appear to be correct:

Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 27 2018, 04:59:51) [MSC v.1914 64 bit (AMD64)] on win32   
Type "help", "copyright", "credits" or "license" for more information.                        
>>> from tabulate import tabulate
>>> table = [["eggs",451],["more\nspam",42]]
>>> print(tabulate(table, tablefmt="github"))
|-----------|-----|
| eggs      | 451 |
| more
spam |  42 |
>>>

I get the same output for Python 3.8.0.

display np.int64 numbers as integers, do not apply 'floatfmt'

This is more a result of how pandas data frames are stored internally, but the issue comes up when you tabulate any dataframe that contains float type columns. When the df is converted to a numpy array, numpy will upcast all int values to floats.

from tabulate import tabulate
import pandas as pd
df = pd.DataFrame({'A':[1,2,3,4],'B':[.20942, .092384, .019823, .01923]})
print(tabulate(df, floatfmt='.2f', headers=df.columns, tablefmt='pipe'))
|    |    A |    B |
|---:|-----:|-----:|
|  0 | 1.00 | 0.21 |
|  1 | 2.00 | 0.09 |
|  2 | 3.00 | 0.02 |
|  3 | 4.00 | 0.02 |

This can further be shown that it's caused by internal storage of np.int64 dtype:

print(tabulate([['int','float'],[np.int64(1), 3.14159]], headers='firstrow',tablefmt='pipe', floatfmt='.2f'))
|   int |   float |
|------:|--------:|
|  1.00 |    3.14 |

Is there a safe way to convert int64 types to vanilla int when parsing dataframes? Maybe there is a workaround i'm not thinking of.

PS - Note that np.int64(1) == int(1) evaluates to True

`floatfmt` doesn't work when `tablefmt="pretty"`

Reproduced with tabulate==0.8.7

import tabulate


table = tabulate.tabulate(
    [
        [1.10000001],
    ],
    tablefmt="pretty",
    floatfmt=".3f",
)

print(table)

yields

+------------+
| 1.10000001 |
+------------+

instead of

+-------+
| 1.100 |
+-------+

An option to add a title above the table

Thank you for this amazing python-tabulate tool.

I was thinking that it'd be great if there is an option to add a title at the centre above the table. I have a pull request for this and I attach the link here: #9

I have not done much of creating issues/making pull requests so I apologize in advance that I made a pull request first and created a new issue. I'd greatly appreciate any feedback. Thank you.

Custom TableFormat gives: TypeError: unhashable type: 'list'

Cloned tabulate from master (433dfc6) and tried to use custom TableFormat:

Script:

from tabulate import tabulate, TableFormat, Line, DataRow

tablefmt = TableFormat(
        lineabove        = Line("", "-", "  ", ""),
        linebelowheader  = Line("", "-", "  ", ""),
        linebetweenrows  = None,
        linebelow        = Line("", "-", "  ", ""),
        headerrow        = DataRow("", "  ", ""),
        datarow          = DataRow("", "  ", ""),
        padding          = 0,
        with_header_hide = ["lineabove", "linebelow"])

rows = [
    ['foo', 'bar'],
    ['baz', 'qux'] ]

print(tabulate(rows, headers=['A', 'B'], tablefmt=tablefmt))

Output:

Traceback (most recent call last):
  File "<stdin>", line 17, in <module>
  File "/home/woky/work/test/venv/lib/python3.7/site-packages/tabulate.py", line 1268, in tabulate
    if tablefmt in multiline_formats and _is_multiline(plain_text):
TypeError: unhashable type: 'list'

(I'm not the original issue submitter - this is copied from https://bitbucket.org/astanin/python-tabulate/issues/156/custom-tableformat-gives-typeerror)

0.8.7 broke html rows in tables

So I have an application which is doing the proper html escaping of row items.

In the new 0.8.7 release it seems now those rows are getting html escaped automatically in html tables.

Is there anyway we can make the html escaping an option that can be turned off (default to on) so that people that know what they are doing can include html in tables without issue...

alignment error with multiline and unicode

hello :) I found some bug while using alignment and multiline with unicode(Korean)
below is simple bug code.

Tests (test.py)

import tabulate
print(tabulate.WIDE_CHARS_MODE)
 
header = ['Something\nLength', 'Is\nDifferent']
data = [['유니코드\nKorean', 'Is\nDifferent']] # this is Korean
print(tabulate.tabulate(data, header, tablefmt='fancy_grid', stralign='center'))

Result
Screenshot from 2019-12-17 16-48-45

Is there some problem with using multiline and alignment with unicode together?
Or there is something wrong with my environment?

left, right, and center-aligned Markdown tables

I realize that Markdown is a mess, but for tablefmt="github" with stralign argument, the header line (which goes as -----------) can be modified as the following:

  • stralign="left": header lines will have :---------.
  • stralign="right": header lines will have ---------:.
  • stralign="center": header lines have :--------:.

Allow custom column padding

There are many tablefmt options, but they all have 2 spaces between columns. Can this be configured?

For example,

from tabulate import tabulate
print(tabulate([[1, 2], [3, 4]], tablefmt='plain'))

produces

1  2
3  4

My proposal is to have options padding, left_padding and right_padding like PrettyTable has

from tabulate import tabulate
print(tabulate([[1, 2], [3, 4]], tablefmt='plain', left_padding=0))

produces

1 2
3 4

Feature request: colalign in github/md formatted table

Column alignment in MD/github formatted table appears to not work. Example to reproduce:

$ python3
>>> import tabulate
>>> table = [["spam",42],["eggs",451],["bacon",0]]
>>> table = [["spam",42],["eggs",451],["bacon",0]]
>>> headers = ["item", "qty"]
>>> align = ["center", "right"]
>>> print(tabulate.tabulate(table, headers, tablefmt="github", colalign=align))
|  item  | qty   |
|--------|-------|
|  spam  |    42 |
|  eggs  |   451 |
| bacon  |     0 |
>>> 

Expected:
|  item  | qty   |
|:------:|------:|        # required colons ':' missing
|  spam  |    42 |
|  eggs  |   451 |
| bacon  |     0 |

This is perhaps easy to fix.

Thanks
Sri

Feature Request: Support a list of headers when passing a list of dicts

This is an awesome library!

I have a use case that's not quite covered right now. I often have a giant list of dictionaries, where each dictionary has about ~400 key:value pairs.

(Ex. I want it to ignore the data under ignore_this_key and and_this_one)

tasks = [
        {'id': 1, 'name': 1, 'ignore_this_key': 1, 'and_this_one': 1},
        {'id': 2, 'name': 2, 'ignore_this_key': 2, 'and_this_one': 2}
    ]

What I'd like to be able to do is pass a list or dictionary of headers and get an output that shows only the keys I specified in the headers (if I include all the keys as columns, it's way too beefy for my terminal)

Ex.

  id    name
   1       1
   2       2

But right now, if I call tabulate.tabulate(tasks, headers={'id': 'id', 'name': 'name'}), I get:

  ignore_this_key    and_this_one    id    name
                1               1     1       1
                2               2     2       2

I wrote a little function that does what I want, but it would be more elegant if it was in tabulate itself:

import tabulate

tasks = [
        {'id': 1, 'name': 1, 'ignore_this_key': 1, 'and_this_one': 1},
        {'id': 2, 'name': 2, 'ignore_this_key': 2, 'and_this_one': 2}
    ]

def task_table(task_data, headers):
    result_data = [{k: t[k] for k in t if k in headers} for t in task_data]
    if not isinstance(headers, dict):
        headers = {h: h for h in headers}
    return tabulate.tabulate(result_data, headers=headers)

print(task_table(tasks, ['id', 'name']))

If this seems reasonable, I'll take a crack at a PR for it 😄.
If there's already a way I can do this, apologies.

Multiline cells github formatting broken

Multiline cell formatting as described in the documentation appears to be broken for the tablefmt="github" option. Python version 2.7.10, tabulate version 0.8.5.

Using the following script:

$ cat tst.py 
from __future__ import print_function
import tabulate

print(tabulate.__version__)

table = [["eggs",451],["more\nspam",42]]
headers = ["item\nname", "qty"]

print(tabulate.tabulate(table, headers, tablefmt="github"))

Output:

$ python -V
Python 2.7.10
$ python /Users/rjgildea/software/cctbx/build/tst.py 
0.8.5
| item
name      |   qty |
|------|-------|
| eggs |   451 |
| more
spam      |    42 |

Expected output according to the documentation:

>>> print(tabulate(table, headers, tablefmt="github"))
| item   | qty   |
| name   |       |
|--------|-------|
| eggs   | 451   |
| more   | 42    |
| spam   |       |

Improper pipe formatting on blank tables with headers

Using tabulate v0.8.5 and python v3.6.8.

Calling tabulate with an empty data frame and header values passed in generates invalid Markdown for the pipe format. A call like:

tabulate( emptyTable, headers=emptyTable.columns.values, tablefmt="pipe", showindex=False )

creates the following output:

| col1  | col2   | col3   | col4   | col5   |
||

tabulate version 0.8.4 installation on Windows results in UnicodeDecodeError

Hi, my team is seeing issues installing version 0.8.4 (it's a dependency of one of the other pypi packages we use).

UnicodeEncodeError: 'charmap' codec can't encode characters in position 5907-5924: character maps to <undefined>

(env) PS C:\Users\wchill\Projects> pip install tabulate
Collecting tabulate
  Downloading https://files.pythonhosted.org/packages/76/35/ae65ed1268d6e2a1be141723e5fffdf4a28e4f4e7c1e083709b308998f90/tabulate-0.8.4.tar.gz (45kB)
     |████████████████████████████████| 51kB 1.1MB/s
    ERROR: Command errored out with exit status 1:
     command: 'c:\users\wchill\projects\env\scripts\python.exe' -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'C:\\Users\\WCHILL\\AppData\\Local\\Temp\\pip-install-u6zf4jij\\tabulate\\setup.py'"'"'; __file__='"'"'C:\\Users\\WCHILL\\AppData\\Local\\Temp\\pip-install-u6zf4jij\\tabulate\\setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' egg_info --egg-base pip-egg-info
         cwd: C:\Users\WCHILL\AppData\Local\Temp\pip-install-u6zf4jij\tabulate\
    Complete output (7 lines):
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "C:\Users\WCHILL\AppData\Local\Temp\pip-install-u6zf4jij\tabulate\setup.py", line 25, in <module>
        f.write(LONG_DESCRIPTION)
      File "c:\users\wchill\projects\env\lib\encodings\cp1252.py", line 19, in encode
        return codecs.charmap_encode(input,self.errors,encoding_table)[0]
    UnicodeEncodeError: 'charmap' codec can't encode characters in position 5907-5924: character maps to <undefined>
    ----------------------------------------
ERROR: Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.

tabulate stops printing table inbetween when processing list with many entries

Hi,

Im using tabulate to display config data which is stored in a list. When the list has a lot of entries tabulate doesnt display them all and stops somewhere in between. Also the point
of stopping is different when I use tablefmt='fancy_grid' for example.
Ive tried to combine the screenshots so you can see that the output stops at "interface GigabitEthernet2/0/23" although the list contains a lot more elements.

Thanks for the very useful lib

Version: Python 3.7.4 tabulate 0.8.6
...
print (dot1x_results)
print (tabulate(dot1x_results, headers="firstrow", tablefmt='github'))
....
grafik

Feature request: Insert a divider

It would be great to be able to add a dividing line at arbitrary points within a table. For example:

import tabulate

rows = []
rows.append([1, 2, 3])
rows.append([4, 5, 6])
rows.append(tabulate.DIVIDER)
rows.append([5, 7, 9])
print(tabulate.tabulate(rows, tablefmt='psql'))

Desired output:

+---+---+---+
| 1 | 2 | 3 |
| 4 | 5 | 6 |
+---+---+---+
| 5 | 7 | 9 |
+---+---+---+

alignment error

Hi,

My code is like this:

from tabulate import tabulate


res = [{'object': '1986', 'relation': '出生日期', 'subject': '爱德华·尼科·埃尔南迪斯'}, {'object': '哥伦比亚', 'relation': '国籍', 'subject': '爱德华·尼科·埃尔南迪斯'}, {'object': '70公分', 'relation': '身高', 'subject': '爱德华·尼科·埃尔南迪斯'}]

title = ['subject', 'relation', 'object']
tbl = [title] + [[el['subject'], el['relation'], el['object']] for el in res]
tbl = tabulate(tbl, headers='firstrow', tablefmt='orgtbl', colalign=('right',))

print(tbl)

And the result is:

|      subject | relation   | object   |
|--------------+------------+----------|
| 爱德华·尼科·埃尔南迪斯 | 出生日期       | 1986     |
| 爱德华·尼科·埃尔南迪斯 | 国籍         | 哥伦比亚     |
| 爱德华·尼科·埃尔南迪斯 | 身高         | 70公分     |

How could I make the column aligns well ?

Feature Request: Enable alignment for header

Existing code supports numalign and stralign for aligning numbers and strings. It would be helpful if the header is allowed to have a alignment.

Example:

+---------------------+-------------+
| message id          |  occur      |
+---------------------+-------------+
| trailing-whitespace |.         1  |
+---------------------+-------------+
| too-many-statemen   |.         1  |
+---------------------+-------------+
|too-many-locals      |.          1 |
+---------------------+-------------+

If the headers are aligned (ex. center)

+---------------------+-------------+
|     message id      |    occur    |
+---------------------+-------------+
| trailing-whitespace |.         1  |
+---------------------+-------------+
| too-many-statemen   |.         1  |
+---------------------+-------------+
|too-many-locals      |.          1 |
+---------------------+-------------+

Fix all DeprecationWarning from within tabulate

@astanin I salute the move from bitbucket to git and github as it makes the project clearly more accesible (and easier to contribute back).

I have a strong interest in keeping tabulate maintained because I use it in https://github.com/ansible/molecule/ and currently I am facing deprecation warning from inside tabulate. Unless tabulate regains its maintenance status, I will be forced to either looks for a replacement or to even fork it. Example tabulate.py:1258: DeprecationWarning: invalid escape sequence \e -- which probably was fixed in master but without are pypi release that is of no help.

If you need help with maintenance, especially CI, packaging, release, let me know. BTW, it would be a good idea to move the repository under an organization so it would not be dependent only on you. For example I am using https://github.com/pycontribs/ for lots of projects and ensure that projects do not have a single human point of failure ;)

Cannot install on Windows 10

Version
Python 3.8.1

What did you do?
pip3 install tabulate

What did you expected to see?
Installed successfully

What did you see instead

    ERROR: Command errored out with exit status 1:
     command: 'c:\users\home\appdata\local\programs\python\python37\python.exe' -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'C:\\Users\\Home\\AppData\\Local\\Temp\\pip-install-0lcew9df\\tabulate\\setup.py'"'"'; __file__='"'"'C:\\Users\\Home\\AppData\\Local\\Temp\\pip-install-0lcew9df\\tabulate\\setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' egg_info --egg-base 'C:\Users\Home\AppData\Local\Temp\pip-install-0lcew9df\tabulate\pip-egg-info'
         cwd: C:\Users\Home\AppData\Local\Temp\pip-install-0lcew9df\tabulate\
    Complete output (26 lines):
    Traceback (most recent call last):
      File "c:\users\home\appdata\local\programs\python\python37\lib\site-packages\pkg_resources\__init__.py", line 2798, in get_entry_map
        ep_map = self._ep_map
      File "c:\users\home\appdata\local\programs\python\python37\lib\site-packages\pkg_resources\__init__.py", line 2756, in __getattr__
        raise AttributeError(attr)
    AttributeError: _ep_map

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "C:\Users\Home\AppData\Local\Temp\pip-install-0lcew9df\tabulate\setup.py", line 64, in <module>
        test_suite="nose.collector",
      File "c:\users\home\appdata\local\programs\python\python37\lib\site-packages\setuptools\__init__.py", line 144, in setup
        _install_setup_requires(attrs)
      File "c:\users\home\appdata\local\programs\python\python37\lib\site-packages\setuptools\__init__.py", line 133, in _install_setup_requires
        (k, v) for k, v in attrs.items()
      File "c:\users\home\appdata\local\programs\python\python37\lib\site-packages\setuptools\dist.py", line 444, in __init__
        for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'):
      File "c:\users\home\appdata\local\programs\python\python37\lib\site-packages\pkg_resources\__init__.py", line 656, in <genexpr>
        for entry in dist.get_entry_map(group).values()
      File "c:\users\home\appdata\local\programs\python\python37\lib\site-packages\pkg_resources\__init__.py", line 2801, in get_entry_map
        self._get_metadata('entry_points.txt'), self
      File "c:\users\home\appdata\local\programs\python\python37\lib\site-packages\pkg_resources\__init__.py", line 2499, in parse_map
        raise ValueError("Entry points must be listed in groups")
    ValueError: Entry points must be listed in groups
    ----------------------------------------
ERROR: Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.

Feature Request: Support for "footer" or "total" row

It would be nice to have a footer or total row, that is visually seperated from the rest of the data (like the header). The difference to the header is that numbers should be formatted like the other rows and placed in relation to it as well.

Release 0.8.6 (fix deprecation warnings)

/Users/ssbarnea/.pyenv/versions/3.7.4/lib/python3.7/site-packages/tabulate.py:415
  /Users/ssbarnea/.pyenv/versions/3.7.4/lib/python3.7/site-packages/tabulate.py:415: DeprecationWarning: invalid escape sequence \[
    _invisible_codes_bytes = re.compile(b"\x1b\[\d+[;\d]*m|\x1b\[\d*\;\d*\;\d*m")  # ANSI color codes

The code in master does not have this problem but we need 0.8.6 tag added and publish to pypi.

PRESERVE_WHITESPACE should be made an argument

It's currently a module-level global variable. This is assuming that all users of tabulate uses the same PRESERVE_WHITESPACE setting. This will not work well if different use of tabulate needs different settings.

Having an argument for this setting would be more general.

unsafehtml is not creating html at all

This is the result of trying unsafehtml on the python repl:

Python 3.7.7 (default, Mar 10 2020, 15:16:38)
[GCC 7.5.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> table = [["spam",42],["eggs",451],["bacon",0]]
>>> import tabulate
>>> headers = ["item", "qty"]
>>> print(tabulate.tabulate(table, headers, tablefmt="html"))
<table>
<thead>
<tr><th>item  </th><th style="text-align: right;">  qty</th></tr>
</thead>
<tbody>
<tr><td>spam  </td><td style="text-align: right;">   42</td></tr>
<tr><td>eggs  </td><td style="text-align: right;">  451</td></tr>
<tr><td>bacon </td><td style="text-align: right;">    0</td></tr>
</tbody>
</table>
>>> print(tabulate.tabulate(table, headers, tablefmt="unsafehtml"))
item      qty
------  -----
spam       42
eggs      451
bacon       0
>>> tabulate.__version__
'0.8.7'

As you can see there isn't any html at all on the result.
In that case I would expect exactly the same result as the html version.

I am using tabulate: 0.8.7

Doesn't work with ASCII escapes (i.e. colors)

When I use tabulate with color sequences, all of the data doesn't line up with the headers.
Here are examples:

Colorized

Assignment                                                                             %        Points      Max Pts.  Comment                        Category
-------------------------------------------------------------------------------------  -------  --------  ----------  -----------------------------  --------------------------------------------------------------------------------
L7 Quiz, L7 PY, translation, questions, P46 to P49 ALL SECTIONS                   E        -                200                                 Quizzes for Chinese classes / Keyboarding and Weekly Planner check for CCP class
P36 & L6 Pinyin, Translation & Qs, P38 to P43 ALL SECTIONS!                       E        -                200                                 Quizzes for Chinese classes / Keyboarding and Weekly Planner check for CCP class

Uncolorized

Assignment                                                                        %        Points      Max Pts.  Comment                        Category
--------------------------------------------------------------------------------  -------  --------  ----------  -----------------------------  --------------------------------------------------------------------------------
L7 Quiz, L7 PY, translation, questions, P46 to P49 ALL SECTIONS                   E        -                200                                 Quizzes for Chinese classes / Keyboarding and Weekly Planner check for CCP class
P36 & L6 Pinyin, Translation & Qs, P38 to P43 ALL SECTIONS!                       E        -                200                                 Quizzes for Chinese classes / Keyboarding and Weekly Planner check for CCP class

Please modify publishing to include wheel

Do this instead: python3 setup.py sdist bdist_wheel

Here's the rationale:
https://www.python.org/dev/peps/pep-0427/#rationale
https://stackoverflow.com/questions/31401762/python-packaging-wheels-vs-tarball-tar-gz

This library can't work with some platform specific codebases until it's built like that:

$pip download "tabulate"=="0.8.6" --dest deps-win32 --platform win32 --python-version 36
...
ERROR: When restricting platform and interpreter constraints using --python-version, --platform, --abi, or --implementation, either --no-deps must be set, or --only-binary=:all: must be set and --no-binary must not be set (or must be set to :none:).

$pip download "tabulate"=="0.8.6" --dest deps-win32 --platform win32 --python-version 36 --only-binary=:all:
...
ERROR: Could not find a version that satisfies the requirement tabulate==0.8.6 (from versions: 0.7.6b0, 0.7.7)
ERROR: No matching distribution found for tabulate==0.8.6

implement integer format specifier (intfmt) with group delimiters

In some cases integers may also need formatting, e.g. to break long integer with spaces or commas.

>>> print(tabulate([dict(a=123456789, b=123456789.0)], headers="keys", floatfmt=",.0f"))
        a            b
---------  -----------
123456789  123,456,789

I think tabular could benefit from intfmt parameter to avoid breaking existing behavior.

Thanks for a great lib!

Corrupts Application

Hi,
I have a bug that breaks the whole application. (python 3.7, tabulate 0.8.6)

I removed as much code and table text as possible.
crash.py

from tabulate import tabulate
table = [
    ['1295-kein_.pdf', ''],
    ['Ausrüstungsliste', '']]
print(tabulate(table))
print('Executed')
import json
print('Not executed')

My output

----------------
1295-kein_.pdf
Ausrüstungsliste
----------------
Executed

Process finished with exit code -1073741819 (0xC0000005)

implicit Float styling reformat applied on string values

Ive had this issue with strings being reformated by tabulate.

>>> sample =  [[f"{1.202:0.2f}",f"{1.212:0.2f}"]]
>>> sample
[['1.20', '1.21']]

We have a sample of 2 strings that were formatted from 2 floats to make two '0' padded strings

after tabulate we have truncation of the 0 padding on the 1st string.

>>> print(tabulate(sample))
---  ----
1.2  1.21
---  ----

Its probably minor but I don't think it should do that on a string value. So I would mark it as a bug. Otherwise I like it on small result sets.

Feature Request: Date and datetime column formatting

It appears that datetime.datetime and datetime.date objects are considered the text type as noted in tabulate.py#L646-L647 and tabulate.py#L887-L889.

This request is for the user to be able to pass the format for date/time columns into floatfmt using standard strftime notation (e.g. "%b %Y" yields "Jun 2020"), like this:

>>> print(
        tabulate.tabulate([
            [datetime.date(2020,6,1), datetime.datetime(2020,6,1,12,0,0)], 
            [datetime.date(2020,6,1), datetime.datetime(2020,6,1,12,0,0)]
        ], floatfmt=("%b %Y", "%B %Y"))
    )

Desired output

----------  -----------
Jun 2020   June 2020
Jun 2020   June 2020
----------  -----------

Actual output

----------  -------------------
2020-06-01  2020-06-01 12:00:00
2020-06-01  2020-06-01 12:00:00
----------  -------------------

Doesn't tabulate correctly with zero-width characters (Devanagari)

This is how it renders

Name            Score
------------  -------
राष्ट्र परीक्षण    19.25
Test             0

versus

Name               Score
---------------  -------
Devanagari here    19.25
Test                0

How it should render:

Name            Score
------------  -------
राष्ट्र परीक्षण         19.25
Test             0

I have the extra widechars libs installed.

Support Python 3.8

Python 3.8 has been released, it's time to support it officially.

New test environments should be added.

Bug: implicit conversion of strings to exponent. (tabulate converting types)

This is a bug is converting strings like this 20200407_2 to exponent values.

>>> print(tabulate.tabulate([ [93, '20200310.2'], [65, '20200407_2'] ]))
--  -----------
93  2.02003e+07
65  2.02004e+08
--  -----------

surprising that float will convert this one at all as it has an underscore in it.

>>> float('20200407_2')
202004072.0

Anyway tabulate should not be converting types at all.

Support hyperlinks encoded as terminal escape sequences

Some terminals support clickable text via escape sequences. If we use the escape sequence

>>> import tabulate
>>>
>>> def encode_url(text: str, target:str):
...     return f"\u001b]8;;{target}\u001b\\{text}\u001b]8;;\u001b\\"
... 
>>> table = tabulate.tabulate([[encode_url("test", "http://example.org")]], headers=["test"], tablefmt="grid")
>>> print(table)
+--------+
| test   |
+========+
| test        |
+--------+

tabulate does not calculate the width correctly.

ENH,BUG,SEC: Jupyter support, HTML escaping

I created a wrapper class to display HTML in JupyterLab; and then looked at the source and realized there's no cgi.escape / html.escape / bleach.clean / bleach.linkify; which (I assume) is an XSS vulnerability

class TabulateHTML:
    def __init__(self, *args, **kwargs):
        kwargs['tablefmt'] = 'html'
        self.html = tabulate(*args, **kwargs)
    def _repr_html_(self):
        return self.html
TabulateHTML(output)

There's likely a better way to wrap TableFormat to return either an object with a .repr_html() method or an IPython.display.HTML when tablefmt='jupyterhtml' | 'jupyter' | 'ipython'?

https://ipython.readthedocs.io/en/stable/config/integrating.html#rich-display

https://ipython.readthedocs.io/en/stable/api/generated/IPython.display.html#IPython.display.HTML

(edit)
Pull Requests:

  • #26 BUG,SEC: html.escape to prevent XSS
  • #27 ENH: return a wrapped str w/ a repr_html so that Jupyter displays the html

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.