tabulate-0.7.7/0000777000000000000000000000000013010122452011446 5ustar 00000000000000tabulate-0.7.7/LICENSE0000666000000000000000000000207312674206067012501 0ustar 00000000000000Copyright (c) 2011-2016 Sergey Astanin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. tabulate-0.7.7/MANIFEST.in0000666000000000000000000000006512674206067013231 0ustar 00000000000000include LICENSE include README include README.rst tabulate-0.7.7/PKG-INFO0000666000000000000000000005665213010122452012561 0ustar 00000000000000Metadata-Version: 1.1 Name: tabulate Version: 0.7.7 Summary: Pretty-print tabular data Home-page: https://bitbucket.org/astanin/python-tabulate Author: Sergey Astanin Author-email: s.astanin@gmail.com License: Copyright (c) 2011-2016 Sergey Astanin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Description: =============== 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:\Python27\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 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) * two-dimensional NumPy array * NumPy record arrays (names as columns) * pandas.DataFrame Examples in this file use Python2. Tabulate supports Python3 too. 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" - "grid" - "fancy_grid" - "pipe" - "orgtbl" - "jira" - "psql" - "rst" - "mediawiki" - "moinmoin" - "html" - "latex" - "latex_booktabs" - "textile" ``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 ``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 | +--------+-------+ ``fancy_grid`` draws a grid using box-drawing characters:: >>> print tabulate(table, headers, tablefmt="fancy_grid") ╒════════╤═══════╕ │ item │ qty │ ╞════════╪═══════╡ │ spam │ 42 │ ├────────┼───────┤ │ eggs │ 451 │ ├────────┼───────┤ │ bacon │ 0 │ ╘════════╧═══════╛ ``psql`` is like tables formatted by Postgres' psql cli:: >>> print tabulate.tabulate() +--------+-------+ | 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 | ``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;" |+ |- ! item !! align="right"| qty |- | spam || align="right"| 42 |- | eggs || align="right"| 451 |- | bacon || align="right"| 0 |} ``moinmoin`` format produces a table markup used in `MoinMoin` wikis:: >>> print tabulate(d,headers,tablefmt="moinmoin") || ''' 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:: >>> print tabulate(table, headers, tablefmt="html")
item qty
spam 42
eggs 451
bacon 0
``latex`` format creates a ``tabular`` environment for LaTeX markup:: >>> print tabulate(table, headers, tablefmt="latex") \begin{tabular}{lr} \hline item & qty \\ \hline spam & 42 \\ eggs & 451 \\ bacon & 0 \\ \hline \end{tabular} ``latex_booktabs`` creates a ``tabular`` environment for LaTeX markup using spacing and style from the ``booktabs`` package. .. _Pandoc Markdown extensions: http://johnmacfarlane.net/pandoc/README.html#tables .. _PHP Markdown Extra: http://michelf.ca/projects/php-markdown/extra/#table .. _table.el: http://table.sourceforge.net/ .. _org-mode: http://orgmode.org/manual/Tables.html .. _reStructuredText: http://docutils.sourceforge.net/docs/user/rst/quickref.html#tables .. _Textile: http://redcloth.org/hobix.com/textile/ .. _Wikipedia: http://www.mediawiki.org/wiki/Help:Tables 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 ---- ---- 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 -- ------ 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 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) -f FMT, --format FMT set output table format; supported formats: plain, simple, grid, fancy_grid, pipe, orgtbl, rst, mediawiki, html, latex, latex_booktabs, 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, coma or other separator. In 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`` :: ================================= ========== =========== Table formatter time, μs rel. time ================================= ========== =========== csv to StringIO 25.3 1.0 join with tabs and newlines 33.6 1.3 asciitable (0.8.0) 590.0 23.4 tabulate (0.7.7) 1403.5 55.6 tabulate (0.7.7, WIDE_CHARS_MODE) 2156.6 85.4 PrettyTable (0.7.2) 3377.0 133.7 texttable (0.8.6) 3986.3 157.8 ================================= ========== =========== Version history --------------- - 0.8: FUTURE RELEASE - 0.7.6: Bug fixes. New table formats (``psql``, ``jira``, ``moinmoin``, ``textile``). Wide character support. Printing from database cursors. Option to print row indices. Boolean columns. Ragged rows. Option to disable number parsing. - 0.7.5: Bug fixes. ``--float`` format option for the command line utility. - 0.7.4: Bug fixes. ``fancy_grid`` and ``html`` formats. Command line utility. - 0.7.3: Bug fixes. Python 3.4 support. Iterables of dicts. ``latex_booktabs`` format. - 0.7.2: Python 3.2 support. - 0.7.1: Bug fixes. ``tsv`` format. Column alignment can be disabled. - 0.7: ``latex`` tables. Printing lists of named tuples and NumPy record arrays. Fix printing date and time values. Python <= 2.6.4 is supported. - 0.6: ``mediawiki`` tables, bug fixes. - 0.5.1: Fix README.rst formatting. Optimize (performance similar to 0.4.4). - 0.5: ANSI color sequences. Printing dicts of iterables and Pandas' dataframes. - 0.4.4: Python 2.6 support. - 0.4.3: Bug fix, None as a missing value. - 0.4.2: Fix manifest file. - 0.4.1: Update license and documentation. - 0.4: Unicode support, Python3 support, ``rst`` tables. - 0.3: Initial PyPI release. Table formats: ``simple``, ``plain``, ``grid``, ``pipe``, and ``orgtbl``. How to contribute ----------------- Contributions should include tests and an explanation for the changes they propose. Documentation (examples, docstrings, README.rst) should be updated accordingly. This project uses `nose` 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, ``nose`` and ``tox`` are installed, then run ``tox`` in the root of the project source tree. On Linux ``tox`` expects to find executables like ``python2.6``, ``python2.7``, ``python3.4`` etc. On Windows it looks for ``C:\Python26\python.exe``, ``C:\Python27\python.exe`` and ``C:\Python34\python.exe`` respectively. To test only some Python environements, use ``-e`` option. For example, to test only against Python 2.7 and Python 3.4, run:: tox -e py27,py34 in the root of the project source tree. To enable NumPy and Pandas tests, run:: tox -e py27-extra,py34-extra (this may take a long time the first time, because NumPy and Pandas will have to be installed in the new virtual environments) See ``tox.ini`` file to learn how to use ``nosetests`` directly to test individual Python versions. .. _nose: https://nose.readthedocs.org/ .. _tox: http://tox.testrun.org/ 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. Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Topic :: Software Development :: Libraries tabulate-0.7.7/README0000666000000000000000000000001412674206067012345 0ustar 00000000000000./README.rsttabulate-0.7.7/README.rst0000666000000000000000000004311713010122216013141 0ustar 00000000000000=============== 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:\Python27\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 ------------ .. image:: https://drone.io/bitbucket.org/astanin/python-tabulate/status.png :alt: Build status :target: https://drone.io/bitbucket.org/astanin/python-tabulate/latest 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) * two-dimensional NumPy array * NumPy record arrays (names as columns) * pandas.DataFrame Examples in this file use Python2. Tabulate supports Python3 too. 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" - "grid" - "fancy_grid" - "pipe" - "orgtbl" - "jira" - "psql" - "rst" - "mediawiki" - "moinmoin" - "html" - "latex" - "latex_booktabs" - "textile" ``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 ``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 | +--------+-------+ ``fancy_grid`` draws a grid using box-drawing characters:: >>> print tabulate(table, headers, tablefmt="fancy_grid") ╒════════╤═══════╕ │ item │ qty │ ╞════════╪═══════╡ │ spam │ 42 │ ├────────┼───────┤ │ eggs │ 451 │ ├────────┼───────┤ │ bacon │ 0 │ ╘════════╧═══════╛ ``psql`` is like tables formatted by Postgres' psql cli:: >>> print tabulate.tabulate() +--------+-------+ | 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 | ``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;" |+ |- ! item !! align="right"| qty |- | spam || align="right"| 42 |- | eggs || align="right"| 451 |- | bacon || align="right"| 0 |} ``moinmoin`` format produces a table markup used in `MoinMoin`_ wikis:: >>> print tabulate(d,headers,tablefmt="moinmoin") || ''' 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:: >>> print tabulate(table, headers, tablefmt="html")
item qty
spam 42
eggs 451
bacon 0
``latex`` format creates a ``tabular`` environment for LaTeX markup:: >>> print tabulate(table, headers, tablefmt="latex") \begin{tabular}{lr} \hline item & qty \\ \hline spam & 42 \\ eggs & 451 \\ bacon & 0 \\ \hline \end{tabular} ``latex_booktabs`` creates a ``tabular`` environment for LaTeX markup using spacing and style from the ``booktabs`` package. .. _Pandoc Markdown extensions: http://johnmacfarlane.net/pandoc/README.html#tables .. _PHP Markdown Extra: http://michelf.ca/projects/php-markdown/extra/#table .. _table.el: http://table.sourceforge.net/ .. _org-mode: http://orgmode.org/manual/Tables.html .. _reStructuredText: http://docutils.sourceforge.net/docs/user/rst/quickref.html#tables .. _Textile: http://redcloth.org/hobix.com/textile/ .. _Wikipedia: http://www.mediawiki.org/wiki/Help:Tables 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 ---- ---- 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 -- ------ 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 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) -f FMT, --format FMT set output table format; supported formats: plain, simple, grid, fancy_grid, pipe, orgtbl, rst, mediawiki, html, latex, latex_booktabs, 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, coma or other separator. In 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`` :: ================================= ========== =========== Table formatter time, μs rel. time ================================= ========== =========== csv to StringIO 25.3 1.0 join with tabs and newlines 33.6 1.3 asciitable (0.8.0) 590.0 23.4 tabulate (0.7.7) 1403.5 55.6 tabulate (0.7.7, WIDE_CHARS_MODE) 2156.6 85.4 PrettyTable (0.7.2) 3377.0 133.7 texttable (0.8.6) 3986.3 157.8 ================================= ========== =========== Version history --------------- - 0.8: FUTURE RELEASE - 0.7.6: Bug fixes. New table formats (``psql``, ``jira``, ``moinmoin``, ``textile``). Wide character support. Printing from database cursors. Option to print row indices. Boolean columns. Ragged rows. Option to disable number parsing. - 0.7.5: Bug fixes. ``--float`` format option for the command line utility. - 0.7.4: Bug fixes. ``fancy_grid`` and ``html`` formats. Command line utility. - 0.7.3: Bug fixes. Python 3.4 support. Iterables of dicts. ``latex_booktabs`` format. - 0.7.2: Python 3.2 support. - 0.7.1: Bug fixes. ``tsv`` format. Column alignment can be disabled. - 0.7: ``latex`` tables. Printing lists of named tuples and NumPy record arrays. Fix printing date and time values. Python <= 2.6.4 is supported. - 0.6: ``mediawiki`` tables, bug fixes. - 0.5.1: Fix README.rst formatting. Optimize (performance similar to 0.4.4). - 0.5: ANSI color sequences. Printing dicts of iterables and Pandas' dataframes. - 0.4.4: Python 2.6 support. - 0.4.3: Bug fix, None as a missing value. - 0.4.2: Fix manifest file. - 0.4.1: Update license and documentation. - 0.4: Unicode support, Python3 support, ``rst`` tables. - 0.3: Initial PyPI release. Table formats: ``simple``, ``plain``, ``grid``, ``pipe``, and ``orgtbl``. How to contribute ----------------- Contributions should include tests and an explanation for the changes they propose. Documentation (examples, docstrings, README.rst) should be updated accordingly. This project uses `nose`_ 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, ``nose`` and ``tox`` are installed, then run ``tox`` in the root of the project source tree. On Linux ``tox`` expects to find executables like ``python2.6``, ``python2.7``, ``python3.4`` etc. On Windows it looks for ``C:\Python26\python.exe``, ``C:\Python27\python.exe`` and ``C:\Python34\python.exe`` respectively. To test only some Python environements, use ``-e`` option. For example, to test only against Python 2.7 and Python 3.4, run:: tox -e py27,py34 in the root of the project source tree. To enable NumPy and Pandas tests, run:: tox -e py27-extra,py34-extra (this may take a long time the first time, because NumPy and Pandas will have to be installed in the new virtual environments) See ``tox.ini`` file to learn how to use ``nosetests`` directly to test individual Python versions. .. _nose: https://nose.readthedocs.org/ .. _tox: http://tox.testrun.org/ 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. tabulate-0.7.7/setup.cfg0000666000000000000000000000010013010122452013256 0ustar 00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 tabulate-0.7.7/setup.py0000666000000000000000000000432213010122163013160 0ustar 00000000000000#!/usr/bin/env python try: from setuptools import setup except ImportError: from distutils.core import setup from platform import python_version_tuple, python_implementation import os import re LICENSE = open("LICENSE").read() # strip links from the descripton on the PyPI if python_version_tuple()[0] >= '3': LONG_DESCRIPTION = open("README.rst", "r", encoding="utf-8").read().replace("`_", "`") else: LONG_DESCRIPTION = open("README.rst", "r").read().replace("`_", "`") # strip Build Status from the PyPI package try: if python_version_tuple()[:2] >= ('2', '7'): status_re = "^Build status\n(.*\n){7}" LONG_DESCRIPTION = re.sub(status_re, "", LONG_DESCRIPTION, flags=re.M) except TypeError: if python_implementation() == "IronPython": # IronPython doesn't support flags in re.sub (IronPython issue #923) pass else: raise install_options = os.environ.get("TABULATE_INSTALL","").split(",") libonly_flags = set(["lib-only", "libonly", "no-cli", "without-cli"]) if libonly_flags.intersection(install_options): console_scripts = [] else: console_scripts = ['tabulate = tabulate:_main'] setup(name='tabulate', version='0.7.7', description='Pretty-print tabular data', long_description=LONG_DESCRIPTION, author='Sergey Astanin', author_email='s.astanin@gmail.com', url='https://bitbucket.org/astanin/python-tabulate', license=LICENSE, classifiers= [ "Development Status :: 4 - Beta", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Topic :: Software Development :: Libraries" ], py_modules = ['tabulate'], entry_points = {'console_scripts': console_scripts}, extras_require = {'widechars': ['wcwidth']}, test_suite = 'nose.collector') tabulate-0.7.7/tabulate.egg-info/0000777000000000000000000000000013010122452014741 5ustar 00000000000000tabulate-0.7.7/tabulate.egg-info/.PKG-INFO.swp0000666000000000000000000004000013010120704016714 0ustar 00000000000000b0VIM 7.4g XsergeyROCCANERAC:/Users/sergey/Checkout/python-tabulate/tabulate.egg-info/PKG-INFO3210#"! Utp]k^d_-[K2ad]pY7< o U T  Q  G   f U L   D _G>5  .:1 ULrQH?) u;2oI  Mars 3390 641.85 Moon 1737 73.5 Earth 6371 5973.6 Sun 696000 1.9891e+09 ----- ------ ------------- >>> print tabulate(table) ... ["Moon",1737,73.5],["Mars",3390,641.85]] >>> table = [["Sun",696000,1989100000],["Earth",6371,5973.6], >>> from tabulate import tabulate and outputs a nicely formatted plain-text table:: list of lists or another tabular data type as the first argument, The module provides just one function, ``tabulate``, which takes a ------------- Library usage pip install tabulate set TABULATE_INSTALL=lib-only On Windows:: TABULATE_INSTALL=lib-only pip install tabulate To install just the library on Unix-like operating systems:: on Linux and to ``%APPDATA%\Python\Scripts\tabulate.exe`` on Windows. In this case the command line utility will be installed to ``~/.local/bin/tabulate`` pip install tabulate --user You may consider installing the library only for the current user:: installation on Windows (e.g. ``C:\Python27\Scripts\tabulate.exe``). (e.g. ``/usr/bin``); or as ``tabulate.exe`` to ``Scripts`` in your Python The command line utility will be installed as ``tabulate`` to ``bin`` on Linux pip install tabulate To install the Python library and the command line utility, run:: ------------ Installation decimal point column alignment, configurable number formatting, alignment by a * readable presentation of mixed textual and numeric data: smart output formats suitable for further editing or transformation * authoring tabular data for lightweight plain-text markup: multiple formatting is guided by the data itself * printing small tables without hassle: just one function call, The main use cases of the library are: utility. Pretty-print tabular data in Python, a library and a command-line =============== python-tabulateDescription: ===============WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTIONLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTIONNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BEMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE ANDEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OFTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,included in all copies or substantial portions of the Software.The above copyright notice and this permission notice shall bethe following conditions:permit persons to whom the Software is furnished to do so, subject todistribute, sublicense, and/or sell copies of the Software, and towithout limitation the rights to use, copy, modify, merge, publish,"Software"), to deal in the Software without restriction, includinga copy of this software and associated documentation files (thePermission is hereby granted, free of charge, to any person obtainingLicense: Copyright (c) 2011-2016 Sergey AstaninAuthor-email: s.astanin@gmail.comAuthor: Sergey AstaninHome-page: https://bitbucket.org/astanin/python-tabulateSummary: Pretty-print tabular dataVersion: 0.7.6Name: tabulateMetadata-Version: 1.1ad  i@7C P  u F  ~ L   Classifier: Topic :: Software Development :: LibrariesClassifier: Programming Language :: Python :: 3.4Classifier: Programming Language :: Python :: 3.3Classifier: Programming Language :: Python :: 3.2Classifier: Programming Language :: Python :: 2.7Classifier: Programming Language :: Python :: 2.6Classifier: Operating System :: OS IndependentClassifier: License :: OSI Approved :: MIT LicenseClassifier: Development Status :: 4 - BetaPlatform: UNKNOWN Zack Dever. Alexey Ziyangirov, acaird, Cesar Sanchez, naught101, John Vandenberg, Jan Schulz, Simon Percivall, Javier Santacruz López-Cepero, Sam Denton, Stefan Tatschner, Emiel van Miltenburg, Brandon Bennett, Amjith Ramanujam, Zach Dwiel, Frederik Rietdijk, Philipp Bogensberger, Greg (anonymous), Sergey Astanin, Pau Tallada Crespí, Erwin Marsi, Mik Kocikowski, Bill Ryder, ------------ Contributors .. _tox: http://tox.testrun.org/ .. _nose: https://nose.readthedocs.org/ test individual Python versions. See ``tox.ini`` file to learn how to use ``nosetests`` directly to will have to be installed in the new virtual environments)tabulate-0.7.7/tabulate.egg-info/dependency_links.txt0000666000000000000000000000000113010122452021007 0ustar 00000000000000 tabulate-0.7.7/tabulate.egg-info/entry_points.txt0000666000000000000000000000005513010122452020237 0ustar 00000000000000[console_scripts] tabulate = tabulate:_main tabulate-0.7.7/tabulate.egg-info/PKG-INFO0000666000000000000000000005665213010122452016054 0ustar 00000000000000Metadata-Version: 1.1 Name: tabulate Version: 0.7.7 Summary: Pretty-print tabular data Home-page: https://bitbucket.org/astanin/python-tabulate Author: Sergey Astanin Author-email: s.astanin@gmail.com License: Copyright (c) 2011-2016 Sergey Astanin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Description: =============== 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:\Python27\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 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) * two-dimensional NumPy array * NumPy record arrays (names as columns) * pandas.DataFrame Examples in this file use Python2. Tabulate supports Python3 too. 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" - "grid" - "fancy_grid" - "pipe" - "orgtbl" - "jira" - "psql" - "rst" - "mediawiki" - "moinmoin" - "html" - "latex" - "latex_booktabs" - "textile" ``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 ``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 | +--------+-------+ ``fancy_grid`` draws a grid using box-drawing characters:: >>> print tabulate(table, headers, tablefmt="fancy_grid") ╒════════╤═══════╕ │ item │ qty │ ╞════════╪═══════╡ │ spam │ 42 │ ├────────┼───────┤ │ eggs │ 451 │ ├────────┼───────┤ │ bacon │ 0 │ ╘════════╧═══════╛ ``psql`` is like tables formatted by Postgres' psql cli:: >>> print tabulate.tabulate() +--------+-------+ | 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 | ``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;" |+ |- ! item !! align="right"| qty |- | spam || align="right"| 42 |- | eggs || align="right"| 451 |- | bacon || align="right"| 0 |} ``moinmoin`` format produces a table markup used in `MoinMoin` wikis:: >>> print tabulate(d,headers,tablefmt="moinmoin") || ''' 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:: >>> print tabulate(table, headers, tablefmt="html")
item qty
spam 42
eggs 451
bacon 0
``latex`` format creates a ``tabular`` environment for LaTeX markup:: >>> print tabulate(table, headers, tablefmt="latex") \begin{tabular}{lr} \hline item & qty \\ \hline spam & 42 \\ eggs & 451 \\ bacon & 0 \\ \hline \end{tabular} ``latex_booktabs`` creates a ``tabular`` environment for LaTeX markup using spacing and style from the ``booktabs`` package. .. _Pandoc Markdown extensions: http://johnmacfarlane.net/pandoc/README.html#tables .. _PHP Markdown Extra: http://michelf.ca/projects/php-markdown/extra/#table .. _table.el: http://table.sourceforge.net/ .. _org-mode: http://orgmode.org/manual/Tables.html .. _reStructuredText: http://docutils.sourceforge.net/docs/user/rst/quickref.html#tables .. _Textile: http://redcloth.org/hobix.com/textile/ .. _Wikipedia: http://www.mediawiki.org/wiki/Help:Tables 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 ---- ---- 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 -- ------ 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 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) -f FMT, --format FMT set output table format; supported formats: plain, simple, grid, fancy_grid, pipe, orgtbl, rst, mediawiki, html, latex, latex_booktabs, 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, coma or other separator. In 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`` :: ================================= ========== =========== Table formatter time, μs rel. time ================================= ========== =========== csv to StringIO 25.3 1.0 join with tabs and newlines 33.6 1.3 asciitable (0.8.0) 590.0 23.4 tabulate (0.7.7) 1403.5 55.6 tabulate (0.7.7, WIDE_CHARS_MODE) 2156.6 85.4 PrettyTable (0.7.2) 3377.0 133.7 texttable (0.8.6) 3986.3 157.8 ================================= ========== =========== Version history --------------- - 0.8: FUTURE RELEASE - 0.7.6: Bug fixes. New table formats (``psql``, ``jira``, ``moinmoin``, ``textile``). Wide character support. Printing from database cursors. Option to print row indices. Boolean columns. Ragged rows. Option to disable number parsing. - 0.7.5: Bug fixes. ``--float`` format option for the command line utility. - 0.7.4: Bug fixes. ``fancy_grid`` and ``html`` formats. Command line utility. - 0.7.3: Bug fixes. Python 3.4 support. Iterables of dicts. ``latex_booktabs`` format. - 0.7.2: Python 3.2 support. - 0.7.1: Bug fixes. ``tsv`` format. Column alignment can be disabled. - 0.7: ``latex`` tables. Printing lists of named tuples and NumPy record arrays. Fix printing date and time values. Python <= 2.6.4 is supported. - 0.6: ``mediawiki`` tables, bug fixes. - 0.5.1: Fix README.rst formatting. Optimize (performance similar to 0.4.4). - 0.5: ANSI color sequences. Printing dicts of iterables and Pandas' dataframes. - 0.4.4: Python 2.6 support. - 0.4.3: Bug fix, None as a missing value. - 0.4.2: Fix manifest file. - 0.4.1: Update license and documentation. - 0.4: Unicode support, Python3 support, ``rst`` tables. - 0.3: Initial PyPI release. Table formats: ``simple``, ``plain``, ``grid``, ``pipe``, and ``orgtbl``. How to contribute ----------------- Contributions should include tests and an explanation for the changes they propose. Documentation (examples, docstrings, README.rst) should be updated accordingly. This project uses `nose` 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, ``nose`` and ``tox`` are installed, then run ``tox`` in the root of the project source tree. On Linux ``tox`` expects to find executables like ``python2.6``, ``python2.7``, ``python3.4`` etc. On Windows it looks for ``C:\Python26\python.exe``, ``C:\Python27\python.exe`` and ``C:\Python34\python.exe`` respectively. To test only some Python environements, use ``-e`` option. For example, to test only against Python 2.7 and Python 3.4, run:: tox -e py27,py34 in the root of the project source tree. To enable NumPy and Pandas tests, run:: tox -e py27-extra,py34-extra (this may take a long time the first time, because NumPy and Pandas will have to be installed in the new virtual environments) See ``tox.ini`` file to learn how to use ``nosetests`` directly to test individual Python versions. .. _nose: https://nose.readthedocs.org/ .. _tox: http://tox.testrun.org/ 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. Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Topic :: Software Development :: Libraries tabulate-0.7.7/tabulate.egg-info/requires.txt0000666000000000000000000000002513010122452017336 0ustar 00000000000000 [widechars] wcwidth tabulate-0.7.7/tabulate.egg-info/SOURCES.txt0000666000000000000000000000057513010122452016634 0ustar 00000000000000LICENSE MANIFEST.in README README.rst setup.py tabulate.py tabulate.egg-info/.PKG-INFO.swp tabulate.egg-info/PKG-INFO tabulate.egg-info/SOURCES.txt tabulate.egg-info/dependency_links.txt tabulate.egg-info/entry_points.txt tabulate.egg-info/requires.txt tabulate.egg-info/top_level.txt test/test_api.py test/test_cli.py test/test_input.py test/test_output.py test/test_regression.pytabulate-0.7.7/tabulate.egg-info/top_level.txt0000666000000000000000000000001113010122452017463 0ustar 00000000000000tabulate tabulate-0.7.7/tabulate.py0000666000000000000000000014202313010122175013625 0ustar 00000000000000# -*- coding: utf-8 -*- """Pretty-print tabular data.""" from __future__ import print_function from __future__ import unicode_literals from collections import namedtuple, Iterable from platform import python_version_tuple import re if python_version_tuple()[0] < "3": from itertools import izip_longest from functools import partial _none_type = type(None) _bool_type = bool _int_type = int _long_type = long _float_type = float _text_type = unicode _binary_type = str def _is_file(f): return isinstance(f, file) else: from itertools import zip_longest as izip_longest from functools import reduce, partial _none_type = type(None) _bool_type = bool _int_type = int _long_type = int _float_type = float _text_type = str _binary_type = bytes import io def _is_file(f): return isinstance(f, io.IOBase) try: import wcwidth # optional wide-character (CJK) support except ImportError: wcwidth = None __all__ = ["tabulate", "tabulate_formats", "simple_separated_format"] __version__ = "0.7.7" # minimum extra space in headers MIN_PADDING = 2 # if True, enable wide-character (CJK) support WIDE_CHARS_MODE = wcwidth is not None Line = namedtuple("Line", ["begin", "hline", "sep", "end"]) DataRow = namedtuple("DataRow", ["begin", "sep", "end"]) # A table structure is suppposed to be: # # --- lineabove --------- # headerrow # --- linebelowheader --- # datarow # --- linebewteenrows --- # ... (more datarows) ... # --- linebewteenrows --- # last datarow # --- linebelow --------- # # TableFormat's line* elements can be # # - either None, if the element is not used, # - or a Line tuple, # - or a function: [col_widths], [col_alignments] -> string. # # TableFormat's *row elements can be # # - either None, if the element is not used, # - or a DataRow tuple, # - or a function: [cell_values], [col_widths], [col_alignments] -> string. # # padding (an integer) is the amount of white space around data values. # # with_header_hide: # # - either None, to display all table elements unconditionally, # - or a list of elements not to be displayed if the table has column headers. # TableFormat = namedtuple("TableFormat", ["lineabove", "linebelowheader", "linebetweenrows", "linebelow", "headerrow", "datarow", "padding", "with_header_hide"]) def _pipe_segment_with_colons(align, colwidth): """Return a segment of a horizontal line with optional colons which indicate column's alignment (as in `pipe` output format).""" w = colwidth if align in ["right", "decimal"]: return ('-' * (w - 1)) + ":" elif align == "center": return ":" + ('-' * (w - 2)) + ":" elif align == "left": return ":" + ('-' * (w - 1)) else: return '-' * w def _pipe_line_with_colons(colwidths, colaligns): """Return a horizontal line with optional colons to indicate column's alignment (as in `pipe` output format).""" segments = [_pipe_segment_with_colons(a, w) for a, w in zip(colaligns, colwidths)] return "|" + "|".join(segments) + "|" def _mediawiki_row_with_attrs(separator, cell_values, colwidths, colaligns): alignment = { "left": '', "right": 'align="right"| ', "center": 'align="center"| ', "decimal": 'align="right"| ' } # hard-coded padding _around_ align attribute and value together # rather than padding parameter which affects only the value values_with_attrs = [' ' + alignment.get(a, '') + c + ' ' for c, a in zip(cell_values, colaligns)] colsep = separator*2 return (separator + colsep.join(values_with_attrs)).rstrip() def _textile_row_with_attrs(cell_values, colwidths, colaligns): cell_values[0] += ' ' alignment = { "left": "<.", "right": ">.", "center": "=.", "decimal": ">." } values = (alignment.get(a, '') + v for a, v in zip(colaligns, cell_values)) return '|' + '|'.join(values) + '|' def _html_begin_table_without_header(colwidths_ignore, colaligns_ignore): # this table header will be suppressed if there is a header row return "\n".join(["", ""]) def _html_row_with_attrs(celltag, cell_values, colwidths, colaligns): alignment = { "left": '', "right": ' style="text-align: right;"', "center": ' style="text-align: center;"', "decimal": ' style="text-align: right;"' } values_with_attrs = ["<{0}{1}>{2}".format(celltag, alignment.get(a, ''), c) for c, a in zip(cell_values, colaligns)] rowhtml = "" + "".join(values_with_attrs).rstrip() + "" if celltag == "th": # it's a header row, create a new table header rowhtml = "\n".join(["
", "", rowhtml, "", ""]) return rowhtml def _moin_row_with_attrs(celltag, cell_values, colwidths, colaligns, header=''): alignment = { "left": '', "right": '', "center": '', "decimal": '' } values_with_attrs = ["{0}{1} {2} ".format(celltag, alignment.get(a, ''), header+c+header) for c, a in zip(cell_values, colaligns)] return "".join(values_with_attrs)+"||" def _latex_line_begin_tabular(colwidths, colaligns, booktabs=False): alignment = { "left": "l", "right": "r", "center": "c", "decimal": "r" } tabular_columns_fmt = "".join([alignment.get(a, "l") for a in colaligns]) return "\n".join(["\\begin{tabular}{" + tabular_columns_fmt + "}", "\\toprule" if booktabs else "\hline"]) LATEX_ESCAPE_RULES = {r"&": r"\&", r"%": r"\%", r"$": r"\$", r"#": r"\#", r"_": r"\_", r"^": r"\^{}", r"{": r"\{", r"}": r"\}", r"~": r"\textasciitilde{}", "\\": r"\textbackslash{}", r"<": r"\ensuremath{<}", r">": r"\ensuremath{>}"} def _latex_row(cell_values, colwidths, colaligns): def escape_char(c): return LATEX_ESCAPE_RULES.get(c, c) escaped_values = ["".join(map(escape_char, cell)) for cell in cell_values] rowfmt = DataRow("", "&", "\\\\") return _build_simple_row(escaped_values, rowfmt) def _rst_escape_first_column(rows, headers): def escape_empty(val): if isinstance(val, (_text_type, _binary_type)) and val.strip() is "": return ".." else: return val new_headers = list(headers) new_rows = [] if headers: new_headers[0] = escape_empty(headers[0]) for row in rows: new_row = list(row) if new_row: new_row[0] = escape_empty(row[0]) new_rows.append(new_row) return new_rows, new_headers _table_formats = {"simple": TableFormat(lineabove=Line("", "-", " ", ""), linebelowheader=Line("", "-", " ", ""), linebetweenrows=None, linebelow=Line("", "-", " ", ""), headerrow=DataRow("", " ", ""), datarow=DataRow("", " ", ""), padding=0, with_header_hide=["lineabove", "linebelow"]), "plain": TableFormat(lineabove=None, linebelowheader=None, linebetweenrows=None, linebelow=None, headerrow=DataRow("", " ", ""), datarow=DataRow("", " ", ""), padding=0, with_header_hide=None), "grid": TableFormat(lineabove=Line("+", "-", "+", "+"), linebelowheader=Line("+", "=", "+", "+"), linebetweenrows=Line("+", "-", "+", "+"), linebelow=Line("+", "-", "+", "+"), headerrow=DataRow("|", "|", "|"), datarow=DataRow("|", "|", "|"), padding=1, with_header_hide=None), "fancy_grid": TableFormat(lineabove=Line("╒", "═", "╤", "╕"), linebelowheader=Line("╞", "═", "╪", "╡"), linebetweenrows=Line("├", "─", "┼", "┤"), linebelow=Line("╘", "═", "╧", "╛"), headerrow=DataRow("│", "│", "│"), datarow=DataRow("│", "│", "│"), padding=1, with_header_hide=None), "pipe": TableFormat(lineabove=_pipe_line_with_colons, linebelowheader=_pipe_line_with_colons, linebetweenrows=None, linebelow=None, headerrow=DataRow("|", "|", "|"), datarow=DataRow("|", "|", "|"), padding=1, with_header_hide=["lineabove"]), "orgtbl": TableFormat(lineabove=None, linebelowheader=Line("|", "-", "+", "|"), linebetweenrows=None, linebelow=None, headerrow=DataRow("|", "|", "|"), datarow=DataRow("|", "|", "|"), padding=1, with_header_hide=None), "jira": TableFormat(lineabove=None, linebelowheader=None, linebetweenrows=None, linebelow=None, headerrow=DataRow("||", "||", "||"), datarow=DataRow("|", "|", "|"), padding=1, with_header_hide=None), "psql": TableFormat(lineabove=Line("+", "-", "+", "+"), linebelowheader=Line("|", "-", "+", "|"), linebetweenrows=None, linebelow=Line("+", "-", "+", "+"), headerrow=DataRow("|", "|", "|"), datarow=DataRow("|", "|", "|"), padding=1, with_header_hide=None), "rst": TableFormat(lineabove=Line("", "=", " ", ""), linebelowheader=Line("", "=", " ", ""), linebetweenrows=None, linebelow=Line("", "=", " ", ""), headerrow=DataRow("", " ", ""), datarow=DataRow("", " ", ""), padding=0, with_header_hide=None), "mediawiki": TableFormat(lineabove=Line("{| class=\"wikitable\" style=\"text-align: left;\"", "", "", "\n|+ \n|-"), linebelowheader=Line("|-", "", "", ""), linebetweenrows=Line("|-", "", "", ""), linebelow=Line("|}", "", "", ""), headerrow=partial(_mediawiki_row_with_attrs, "!"), datarow=partial(_mediawiki_row_with_attrs, "|"), padding=0, with_header_hide=None), "moinmoin": TableFormat(lineabove=None, linebelowheader=None, linebetweenrows=None, linebelow=None, headerrow=partial(_moin_row_with_attrs,"||",header="'''"), datarow=partial(_moin_row_with_attrs,"||"), padding=1, with_header_hide=None), "html": TableFormat(lineabove=_html_begin_table_without_header, linebelowheader="", linebetweenrows=None, linebelow=Line("\n
", "", "", ""), headerrow=partial(_html_row_with_attrs, "th"), datarow=partial(_html_row_with_attrs, "td"), padding=0, with_header_hide=["lineabove"]), "latex": TableFormat(lineabove=_latex_line_begin_tabular, linebelowheader=Line("\\hline", "", "", ""), linebetweenrows=None, linebelow=Line("\\hline\n\\end{tabular}", "", "", ""), headerrow=_latex_row, datarow=_latex_row, padding=1, with_header_hide=None), "latex_booktabs": TableFormat(lineabove=partial(_latex_line_begin_tabular, booktabs=True), linebelowheader=Line("\\midrule", "", "", ""), linebetweenrows=None, linebelow=Line("\\bottomrule\n\\end{tabular}", "", "", ""), headerrow=_latex_row, datarow=_latex_row, padding=1, with_header_hide=None), "tsv": TableFormat(lineabove=None, linebelowheader=None, linebetweenrows=None, linebelow=None, headerrow=DataRow("", "\t", ""), datarow=DataRow("", "\t", ""), padding=0, with_header_hide=None), "textile": TableFormat(lineabove=None, linebelowheader=None, linebetweenrows=None, linebelow=None, headerrow=DataRow("|_. ", "|_.", "|"), datarow=_textile_row_with_attrs, padding=1, with_header_hide=None)} tabulate_formats = list(sorted(_table_formats.keys())) _invisible_codes = re.compile(r"\x1b\[\d+[;\d]*m|\x1b\[\d*\;\d*\;\d*m") # ANSI color codes _invisible_codes_bytes = re.compile(b"\x1b\[\d+[;\d]*m|\x1b\[\d*\;\d*\;\d*m") # ANSI color codes def simple_separated_format(separator): """Construct a simple TableFormat with columns separated by a separator. >>> tsv = simple_separated_format("\\t") ; \ tabulate([["foo", 1], ["spam", 23]], tablefmt=tsv) == 'foo \\t 1\\nspam\\t23' True """ return TableFormat(None, None, None, None, headerrow=DataRow('', separator, ''), datarow=DataRow('', separator, ''), padding=0, with_header_hide=None) def _isconvertible(conv, string): try: n = conv(string) return True except (ValueError, TypeError): return False def _isnumber(string): """ >>> _isnumber("123.45") True >>> _isnumber("123") True >>> _isnumber("spam") False """ return _isconvertible(float, string) def _isint(string, inttype=int): """ >>> _isint("123") True >>> _isint("123.45") False """ return type(string) is inttype or\ (isinstance(string, _binary_type) or isinstance(string, _text_type))\ and\ _isconvertible(inttype, string) def _isbool(string): """ >>> _isbool(True) True >>> _isbool("False") True >>> _isbool(1) False """ return type(string) is _bool_type or\ (isinstance(string, (_binary_type, _text_type))\ and\ string in ("True", "False")) def _type(string, has_invisible=True, numparse=True): """The least generic type (type(None), int, float, str, unicode). >>> _type(None) is type(None) True >>> _type("foo") is type("") True >>> _type("1") is type(1) True >>> _type('\x1b[31m42\x1b[0m') is type(42) True >>> _type('\x1b[31m42\x1b[0m') is type(42) True """ if has_invisible and \ (isinstance(string, _text_type) or isinstance(string, _binary_type)): string = _strip_invisible(string) if string is None: return _none_type elif hasattr(string, "isoformat"): # datetime.datetime, date, and time return _text_type elif _isbool(string): return _bool_type elif _isint(string) and numparse: return int elif _isint(string, _long_type) and numparse: return int elif _isnumber(string) and numparse: return float elif isinstance(string, _binary_type): return _binary_type else: return _text_type def _afterpoint(string): """Symbols after a decimal point, -1 if the string lacks the decimal point. >>> _afterpoint("123.45") 2 >>> _afterpoint("1001") -1 >>> _afterpoint("eggs") -1 >>> _afterpoint("123e45") 2 """ if _isnumber(string): if _isint(string): return -1 else: pos = string.rfind(".") pos = string.lower().rfind("e") if pos < 0 else pos if pos >= 0: return len(string) - pos - 1 else: return -1 # no point else: return -1 # not a number def _padleft(width, s): """Flush right. >>> _padleft(6, '\u044f\u0439\u0446\u0430') == ' \u044f\u0439\u0446\u0430' True """ fmt = "{0:>%ds}" % width return fmt.format(s) def _padright(width, s): """Flush left. >>> _padright(6, '\u044f\u0439\u0446\u0430') == '\u044f\u0439\u0446\u0430 ' True """ fmt = "{0:<%ds}" % width return fmt.format(s) def _padboth(width, s): """Center string. >>> _padboth(6, '\u044f\u0439\u0446\u0430') == ' \u044f\u0439\u0446\u0430 ' True """ fmt = "{0:^%ds}" % width return fmt.format(s) def _strip_invisible(s): "Remove invisible ANSI color codes." if isinstance(s, _text_type): return re.sub(_invisible_codes, "", s) else: # a bytestring return re.sub(_invisible_codes_bytes, "", s) def _visible_width(s): """Visible width of a printed string. ANSI color codes are removed. >>> _visible_width('\x1b[31mhello\x1b[0m'), _visible_width("world") (5, 5) """ # optional wide-character support if wcwidth is not None and WIDE_CHARS_MODE: len_fn = wcwidth.wcswidth else: len_fn = len if isinstance(s, _text_type) or isinstance(s, _binary_type): return len_fn(_strip_invisible(s)) else: return len_fn(_text_type(s)) def _align_column(strings, alignment, minwidth=0, has_invisible=True): """[string] -> [padded_string] >>> list(map(str,_align_column(["12.345", "-1234.5", "1.23", "1234.5", "1e+234", "1.0e234"], "decimal"))) [' 12.345 ', '-1234.5 ', ' 1.23 ', ' 1234.5 ', ' 1e+234 ', ' 1.0e234'] >>> list(map(str,_align_column(['123.4', '56.7890'], None))) ['123.4', '56.7890'] """ if alignment == "right": strings = [s.strip() for s in strings] padfn = _padleft elif alignment == "center": strings = [s.strip() for s in strings] padfn = _padboth elif alignment == "decimal": if has_invisible: decimals = [_afterpoint(_strip_invisible(s)) for s in strings] else: decimals = [_afterpoint(s) for s in strings] maxdecimals = max(decimals) strings = [s + (maxdecimals - decs) * " " for s, decs in zip(strings, decimals)] padfn = _padleft elif not alignment: return strings else: strings = [s.strip() for s in strings] padfn = _padright enable_widechars = wcwidth is not None and WIDE_CHARS_MODE if has_invisible: width_fn = _visible_width elif enable_widechars: # optional wide-character support if available width_fn = wcwidth.wcswidth else: width_fn = len s_lens = list(map(len, strings)) s_widths = list(map(width_fn, strings)) maxwidth = max(max(s_widths), minwidth) if not enable_widechars and not has_invisible: padded_strings = [padfn(maxwidth, s) for s in strings] else: # enable wide-character width corrections visible_widths = [maxwidth - (w - l) for w, l in zip(s_widths, s_lens)] # wcswidth and _visible_width don't count invisible characters; # padfn doesn't need to apply another correction padded_strings = [padfn(w, s) for s, w in zip(strings, visible_widths)] return padded_strings def _more_generic(type1, type2): types = { _none_type: 0, _bool_type: 1, int: 2, float: 3, _binary_type: 4, _text_type: 5 } invtypes = { 5: _text_type, 4: _binary_type, 3: float, 2: int, 1: _bool_type, 0: _none_type } moregeneric = max(types.get(type1, 5), types.get(type2, 5)) return invtypes[moregeneric] def _column_type(strings, has_invisible=True, numparse=True): """The least generic type all column values are convertible to. >>> _column_type([True, False]) is _bool_type True >>> _column_type(["1", "2"]) is _int_type True >>> _column_type(["1", "2.3"]) is _float_type True >>> _column_type(["1", "2.3", "four"]) is _text_type True >>> _column_type(["four", '\u043f\u044f\u0442\u044c']) is _text_type True >>> _column_type([None, "brux"]) is _text_type True >>> _column_type([1, 2, None]) is _int_type True >>> import datetime as dt >>> _column_type([dt.datetime(1991,2,19), dt.time(17,35)]) is _text_type True """ types = [_type(s, has_invisible, numparse) for s in strings ] return reduce(_more_generic, types, _bool_type) def _format(val, valtype, floatfmt, missingval="", has_invisible=True): """Format a value accoding to its type. Unicode is supported: >>> hrow = ['\u0431\u0443\u043a\u0432\u0430', '\u0446\u0438\u0444\u0440\u0430'] ; \ tbl = [['\u0430\u0437', 2], ['\u0431\u0443\u043a\u0438', 4]] ; \ good_result = '\\u0431\\u0443\\u043a\\u0432\\u0430 \\u0446\\u0438\\u0444\\u0440\\u0430\\n------- -------\\n\\u0430\\u0437 2\\n\\u0431\\u0443\\u043a\\u0438 4' ; \ tabulate(tbl, headers=hrow) == good_result True """ if val is None: return missingval if valtype in [int, _text_type]: return "{0}".format(val) elif valtype is _binary_type: try: return _text_type(val, "ascii") except TypeError: return _text_type(val) elif valtype is float: is_a_colored_number = has_invisible and isinstance(val, (_text_type, _binary_type)) if is_a_colored_number: raw_val = _strip_invisible(val) formatted_val = format(float(raw_val), floatfmt) return val.replace(raw_val, formatted_val) else: return format(float(val), floatfmt) else: return "{0}".format(val) def _align_header(header, alignment, width, visible_width): "Pad string header to width chars given known visible_width of the header." width += len(header) - visible_width if alignment == "left": return _padright(width, header) elif alignment == "center": return _padboth(width, header) elif not alignment: return "{0}".format(header) else: return _padleft(width, header) def _prepend_row_index(rows, index): """Add a left-most index column.""" if index is None or index is False: return rows if len(index) != len(rows): print('index=', index) print('rows=', rows) raise ValueError('index must be as long as the number of data rows') rows = [[v]+list(row) for v,row in zip(index, rows)] return rows def _bool(val): "A wrapper around standard bool() which doesn't throw on NumPy arrays" try: return bool(val) except ValueError: # val is likely to be a numpy array with many elements return False def _normalize_tabular_data(tabular_data, headers, showindex="default"): """Transform a supported data type to a list of lists, and a list of headers. Supported tabular data types: * list-of-lists or another iterable of iterables * list of named tuples (usually used with headers="keys") * list of dicts (usually used with headers="keys") * list of OrderedDicts (usually used with headers="keys") * 2D NumPy arrays * NumPy record arrays (usually used with headers="keys") * dict of iterables (usually used with headers="keys") * pandas.DataFrame (usually used with headers="keys") The first row can be used as headers if headers="firstrow", column indices can be used as headers if headers="keys". If showindex="default", show row indices of the pandas.DataFrame. If showindex="always", show row indices for all types of data. If showindex="never", don't show row indices for all types of data. If showindex is an iterable, show its values as row indices. """ try: bool(headers) is_headers2bool_broken = False except ValueError: # numpy.ndarray, pandas.core.index.Index, ... is_headers2bool_broken = True headers = list(headers) index = None if hasattr(tabular_data, "keys") and hasattr(tabular_data, "values"): # dict-like and pandas.DataFrame? if hasattr(tabular_data.values, "__call__"): # likely a conventional dict keys = tabular_data.keys() rows = list(izip_longest(*tabular_data.values())) # columns have to be transposed elif hasattr(tabular_data, "index"): # values is a property, has .index => it's likely a pandas.DataFrame (pandas 0.11.0) keys = list(tabular_data) if tabular_data.index.name is not None: if isinstance(tabular_data.index.name, list): keys[:0] = tabular_data.index.name else: keys[:0] = [tabular_data.index.name] vals = tabular_data.values # values matrix doesn't need to be transposed # for DataFrames add an index per default index = list(tabular_data.index) rows = [list(row) for row in vals] else: raise ValueError("tabular data doesn't appear to be a dict or a DataFrame") if headers == "keys": headers = list(map(_text_type,keys)) # headers should be strings else: # it's a usual an iterable of iterables, or a NumPy array rows = list(tabular_data) if (headers == "keys" and not rows): # an empty table (issue #81) headers = [] elif (headers == "keys" and hasattr(tabular_data, "dtype") and getattr(tabular_data.dtype, "names")): # numpy record array headers = tabular_data.dtype.names elif (headers == "keys" and len(rows) > 0 and isinstance(rows[0], tuple) and hasattr(rows[0], "_fields")): # namedtuple headers = list(map(_text_type, rows[0]._fields)) elif (len(rows) > 0 and isinstance(rows[0], dict)): # dict or OrderedDict uniq_keys = set() # implements hashed lookup keys = [] # storage for set if headers == "firstrow": firstdict = rows[0] if len(rows) > 0 else {} keys.extend(firstdict.keys()) uniq_keys.update(keys) rows = rows[1:] for row in rows: for k in row.keys(): #Save unique items in input order if k not in uniq_keys: keys.append(k) uniq_keys.add(k) if headers == 'keys': headers = keys elif isinstance(headers, dict): # a dict of headers for a list of dicts headers = [headers.get(k, k) for k in keys] headers = list(map(_text_type, headers)) elif headers == "firstrow": if len(rows) > 0: headers = [firstdict.get(k, k) for k in keys] headers = list(map(_text_type, headers)) else: headers = [] elif headers: raise ValueError('headers for a list of dicts is not a dict or a keyword') rows = [[row.get(k) for k in keys] for row in rows] elif (headers == "keys" and hasattr(tabular_data, "description") and hasattr(tabular_data, "fetchone") and hasattr(tabular_data, "rowcount")): # Python Database API cursor object (PEP 0249) # print tabulate(cursor, headers='keys') headers = [column[0] for column in tabular_data.description] elif headers == "keys" and len(rows) > 0: # keys are column indices headers = list(map(_text_type, range(len(rows[0])))) # take headers from the first row if necessary if headers == "firstrow" and len(rows) > 0: if index is not None: headers = [index[0]] + list(rows[0]) index = index[1:] else: headers = rows[0] headers = list(map(_text_type, headers)) # headers should be strings rows = rows[1:] headers = list(map(_text_type,headers)) rows = list(map(list,rows)) # add or remove an index column showindex_is_a_str = type(showindex) in [_text_type, _binary_type] if showindex == "default" and index is not None: rows = _prepend_row_index(rows, index) elif isinstance(showindex, Iterable) and not showindex_is_a_str: rows = _prepend_row_index(rows, list(showindex)) elif showindex == "always" or (_bool(showindex) and not showindex_is_a_str): if index is None: index = list(range(len(rows))) rows = _prepend_row_index(rows, index) elif showindex == "never" or (not _bool(showindex) and not showindex_is_a_str): pass # pad with empty headers for initial columns if necessary if headers and len(rows) > 0: nhs = len(headers) ncols = len(rows[0]) if nhs < ncols: headers = [""]*(ncols - nhs) + headers return rows, headers def tabulate(tabular_data, headers=(), tablefmt="simple", floatfmt="g", numalign="decimal", stralign="left", missingval="", showindex="default", disable_numparse=False): """Format a fixed width table for pretty printing. >>> print(tabulate([[1, 2.34], [-56, "8.999"], ["2", "10001"]])) --- --------- 1 2.34 -56 8.999 2 10001 --- --------- The first required argument (`tabular_data`) can be a list-of-lists (or another iterable of iterables), a list of named tuples, a dictionary of iterables, an iterable of dictionaries, a two-dimensional NumPy array, NumPy record array, or a Pandas' dataframe. Table headers ------------- To print nice column headers, supply the second argument (`headers`): - `headers` can be an explicit list of column headers - if `headers="firstrow"`, then the first row of data is used - if `headers="keys"`, then dictionary keys or column indices are used Otherwise a headerless table is produced. If the number of headers is less than the number of columns, they are supposed to be names of the last columns. This is consistent with the plain-text format of R and Pandas' dataframes. >>> print(tabulate([["sex","age"],["Alice","F",24],["Bob","M",19]], ... headers="firstrow")) sex age ----- ----- ----- Alice F 24 Bob M 19 By default, pandas.DataFrame data have an additional column called row index. To add a similar column to all other types of data, use `showindex="always"` or `showindex=True`. To suppress row indices for all types of data, pass `showindex="never" or `showindex=False`. To add a custom row index column, pass `showindex=some_iterable`. >>> print(tabulate([["F",24],["M",19]], showindex="always")) - - -- 0 F 24 1 M 19 - - -- Column alignment ---------------- `tabulate` tries to detect column types automatically, and aligns the values properly. By default it aligns decimal points of the numbers (or flushes integer numbers to the right), and flushes everything else to the left. Possible column alignments (`numalign`, `stralign`) are: "right", "center", "left", "decimal" (only for `numalign`), and None (to disable alignment). Table formats ------------- `floatfmt` is a format specification used for columns which contain numeric data with a decimal point. `None` values are replaced with a `missingval` string: >>> print(tabulate([["spam", 1, None], ... ["eggs", 42, 3.14], ... ["other", None, 2.7]], missingval="?")) ----- -- ---- spam 1 ? eggs 42 3.14 other ? 2.7 ----- -- ---- Various plain-text table formats (`tablefmt`) are supported: 'plain', 'simple', 'grid', 'pipe', 'orgtbl', 'rst', 'mediawiki', 'latex', and 'latex_booktabs'. Variable `tabulate_formats` contains the list of currently supported formats. "plain" format doesn't use any pseudographics to draw tables, it separates columns with a double space: >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], ... ["strings", "numbers"], "plain")) strings numbers spam 41.9999 eggs 451 >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], tablefmt="plain")) spam 41.9999 eggs 451 "simple" format is like Pandoc simple_tables: >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], ... ["strings", "numbers"], "simple")) strings numbers --------- --------- spam 41.9999 eggs 451 >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], tablefmt="simple")) ---- -------- spam 41.9999 eggs 451 ---- -------- "grid" is similar to tables produced by Emacs table.el package or Pandoc grid_tables: >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], ... ["strings", "numbers"], "grid")) +-----------+-----------+ | strings | numbers | +===========+===========+ | spam | 41.9999 | +-----------+-----------+ | eggs | 451 | +-----------+-----------+ >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], tablefmt="grid")) +------+----------+ | spam | 41.9999 | +------+----------+ | eggs | 451 | +------+----------+ "fancy_grid" draws a grid using box-drawing characters: >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], ... ["strings", "numbers"], "fancy_grid")) ╒═══════════╤═══════════╕ │ strings │ numbers │ ╞═══════════╪═══════════╡ │ spam │ 41.9999 │ ├───────────┼───────────┤ │ eggs │ 451 │ ╘═══════════╧═══════════╛ "pipe" is like tables in PHP Markdown Extra extension or Pandoc pipe_tables: >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], ... ["strings", "numbers"], "pipe")) | strings | numbers | |:----------|----------:| | spam | 41.9999 | | eggs | 451 | >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], tablefmt="pipe")) |:-----|---------:| | spam | 41.9999 | | eggs | 451 | "orgtbl" is like tables in Emacs org-mode and orgtbl-mode. They are slightly different from "pipe" format by not using colons to define column alignment, and using a "+" sign to indicate line intersections: >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], ... ["strings", "numbers"], "orgtbl")) | strings | numbers | |-----------+-----------| | spam | 41.9999 | | eggs | 451 | >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], tablefmt="orgtbl")) | spam | 41.9999 | | eggs | 451 | "rst" is like a simple table format from reStructuredText; please note that reStructuredText accepts also "grid" tables: >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], ... ["strings", "numbers"], "rst")) ========= ========= strings numbers ========= ========= spam 41.9999 eggs 451 ========= ========= >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], tablefmt="rst")) ==== ======== spam 41.9999 eggs 451 ==== ======== "mediawiki" produces a table markup used in Wikipedia and on other MediaWiki-based sites: >>> print(tabulate([["strings", "numbers"], ["spam", 41.9999], ["eggs", "451.0"]], ... headers="firstrow", tablefmt="mediawiki")) {| class="wikitable" style="text-align: left;" |+ |- ! strings !! align="right"| numbers |- | spam || align="right"| 41.9999 |- | eggs || align="right"| 451 |} "html" produces HTML markup: >>> print(tabulate([["strings", "numbers"], ["spam", 41.9999], ["eggs", "451.0"]], ... headers="firstrow", tablefmt="html"))
strings numbers
spam 41.9999
eggs 451
"latex" produces a tabular environment of LaTeX document markup: >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], tablefmt="latex")) \\begin{tabular}{lr} \\hline spam & 41.9999 \\\\ eggs & 451 \\\\ \\hline \\end{tabular} "latex_booktabs" produces a tabular environment of LaTeX document markup using the booktabs.sty package: >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], tablefmt="latex_booktabs")) \\begin{tabular}{lr} \\toprule spam & 41.9999 \\\\ eggs & 451 \\\\ \\bottomrule \end{tabular} Number parsing -------------- By default, anything which can be parsed as a number is a number. This ensures numbers represented as strings are aligned properly. This can lead to weird results for particular strings such as specific git SHAs e.g. "42992e1" will be parsed into the number 429920 and aligned as such. To completely disable number parsing (and alignment), use `disable_numparse=True`. For more fine grained control, a list column indices is used to disable number parsing only on those columns e.g. `disable_numparse=[0, 2]` would disable number parsing only on the first and third columns. """ if tabular_data is None: tabular_data = [] list_of_lists, headers = _normalize_tabular_data( tabular_data, headers, showindex=showindex) # empty values in the first column of RST tables should be escaped (issue #82) # "" should be escaped as "\\ " or ".." if tablefmt == 'rst': list_of_lists, headers = _rst_escape_first_column(list_of_lists, headers) # optimization: look for ANSI control codes once, # enable smart width functions only if a control code is found plain_text = '\n'.join(['\t'.join(map(_text_type, headers))] + \ ['\t'.join(map(_text_type, row)) for row in list_of_lists]) has_invisible = re.search(_invisible_codes, plain_text) enable_widechars = wcwidth is not None and WIDE_CHARS_MODE if has_invisible: width_fn = _visible_width elif enable_widechars: # optional wide-character support if available width_fn = wcwidth.wcswidth else: width_fn = len # format rows and columns, convert numeric values to strings cols = list(izip_longest(*list_of_lists)) numparses = _expand_numparse(disable_numparse, len(cols)) coltypes = [_column_type(col, numparse=np) for col, np in zip(cols, numparses)] cols = [[_format(v, ct, floatfmt, missingval, has_invisible) for v in c] for c,ct in zip(cols, coltypes)] # align columns aligns = [numalign if ct in [int,float] else stralign for ct in coltypes] minwidths = [width_fn(h) + MIN_PADDING for h in headers] if headers else [0]*len(cols) cols = [_align_column(c, a, minw, has_invisible) for c, a, minw in zip(cols, aligns, minwidths)] if headers: # align headers and add headers t_cols = cols or [['']] * len(headers) t_aligns = aligns or [stralign] * len(headers) minwidths = [max(minw, width_fn(c[0])) for minw, c in zip(minwidths, t_cols)] headers = [_align_header(h, a, minw, width_fn(h)) for h, a, minw in zip(headers, t_aligns, minwidths)] rows = list(zip(*cols)) else: minwidths = [width_fn(c[0]) for c in cols] rows = list(zip(*cols)) if not isinstance(tablefmt, TableFormat): tablefmt = _table_formats.get(tablefmt, _table_formats["simple"]) return _format_table(tablefmt, headers, rows, minwidths, aligns) def _expand_numparse(disable_numparse, column_count): """ Return a list of bools of length `column_count` which indicates whether number parsing should be used on each column. If `disable_numparse` is a list of indices, each of those indices are False, and everything else is True. If `disable_numparse` is a bool, then the returned list is all the same. """ if isinstance(disable_numparse, Iterable): numparses = [True] * column_count for index in disable_numparse: numparses[index] = False return numparses else: return [not disable_numparse] * column_count def _build_simple_row(padded_cells, rowfmt): "Format row according to DataRow format without padding." begin, sep, end = rowfmt return (begin + sep.join(padded_cells) + end).rstrip() def _build_row(padded_cells, colwidths, colaligns, rowfmt): "Return a string which represents a row of data cells." if not rowfmt: return None if hasattr(rowfmt, "__call__"): return rowfmt(padded_cells, colwidths, colaligns) else: return _build_simple_row(padded_cells, rowfmt) def _build_line(colwidths, colaligns, linefmt): "Return a string which represents a horizontal line." if not linefmt: return None if hasattr(linefmt, "__call__"): return linefmt(colwidths, colaligns) else: begin, fill, sep, end = linefmt cells = [fill*w for w in colwidths] return _build_simple_row(cells, (begin, sep, end)) def _pad_row(cells, padding): if cells: pad = " "*padding padded_cells = [pad + cell + pad for cell in cells] return padded_cells else: return cells def _format_table(fmt, headers, rows, colwidths, colaligns): """Produce a plain-text representation of the table.""" lines = [] hidden = fmt.with_header_hide if (headers and fmt.with_header_hide) else [] pad = fmt.padding headerrow = fmt.headerrow padded_widths = [(w + 2*pad) for w in colwidths] padded_headers = _pad_row(headers, pad) padded_rows = [_pad_row(row, pad) for row in rows] if fmt.lineabove and "lineabove" not in hidden: lines.append(_build_line(padded_widths, colaligns, fmt.lineabove)) if padded_headers: lines.append(_build_row(padded_headers, padded_widths, colaligns, headerrow)) if fmt.linebelowheader and "linebelowheader" not in hidden: lines.append(_build_line(padded_widths, colaligns, fmt.linebelowheader)) if padded_rows and fmt.linebetweenrows and "linebetweenrows" not in hidden: # initial rows with a line below for row in padded_rows[:-1]: lines.append(_build_row(row, padded_widths, colaligns, fmt.datarow)) lines.append(_build_line(padded_widths, colaligns, fmt.linebetweenrows)) # the last row without a line below lines.append(_build_row(padded_rows[-1], padded_widths, colaligns, fmt.datarow)) else: for row in padded_rows: lines.append(_build_row(row, padded_widths, colaligns, fmt.datarow)) if fmt.linebelow and "linebelow" not in hidden: lines.append(_build_line(padded_widths, colaligns, fmt.linebelow)) if headers or rows: return "\n".join(lines) else: # a completely empty table return "" def _main(): """\ Usage: tabulate [options] [FILE ...] Pretty-print tabular data. See also https://bitbucket.org/astanin/python-tabulate 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) -f FMT, --format FMT set output table format; supported formats: plain, simple, grid, fancy_grid, pipe, orgtbl, rst, mediawiki, html, latex, latex_booktabs, tsv (default: simple) """ import getopt import sys import textwrap usage = textwrap.dedent(_main.__doc__) try: opts, args = getopt.getopt(sys.argv[1:], "h1o:s:F:f:", ["help", "header", "output", "sep=", "float=", "format="]) except getopt.GetoptError as e: print(e) print(usage) sys.exit(2) headers = [] floatfmt = "g" tablefmt = "simple" sep = r"\s+" outfile = "-" for opt, value in opts: if opt in ["-1", "--header"]: headers = "firstrow" elif opt in ["-o", "--output"]: outfile = value elif opt in ["-F", "--float"]: floatfmt = value elif opt in ["-f", "--format"]: if value not in tabulate_formats: print("%s is not a supported table format" % value) print(usage) sys.exit(3) tablefmt = value elif opt in ["-s", "--sep"]: sep = value elif opt in ["-h", "--help"]: print(usage) sys.exit(0) files = [sys.stdin] if not args else args with (sys.stdout if outfile == "-" else open(outfile, "w")) as out: for f in files: if f == "-": f = sys.stdin if _is_file(f): _pprint_file(f, headers=headers, tablefmt=tablefmt, sep=sep, floatfmt=floatfmt, file=out) else: with open(f) as fobj: _pprint_file(fobj, headers=headers, tablefmt=tablefmt, sep=sep, floatfmt=floatfmt, file=out) def _pprint_file(fobject, headers, tablefmt, sep, floatfmt, file): rows = fobject.readlines() table = [re.split(sep, r.rstrip()) for r in rows if r.strip()] print(tabulate(table, headers, tablefmt, floatfmt=floatfmt), file=file) if __name__ == "__main__": _main() tabulate-0.7.7/test/0000777000000000000000000000000013010122452012425 5ustar 00000000000000tabulate-0.7.7/test/test_api.py0000666000000000000000000000341013010107455014613 0ustar 00000000000000"""API properties. """ from __future__ import print_function from __future__ import unicode_literals from tabulate import tabulate, tabulate_formats, simple_separated_format from platform import python_version_tuple from common import SkipTest try: if python_version_tuple() >= ('3','3','0'): from inspect import signature, _empty except ImportError: signature = None _empty = None def test_tabulate_formats(): "API: tabulate_formats is a list of strings""" supported = tabulate_formats print("tabulate_formats = %r" % supported) assert type(supported) is list for fmt in supported: assert type(fmt) is type("") def _check_signature(function, expected_sig): if not signature: raise SkipTest() actual_sig = signature(function) print("expected: %s\nactual: %s\n" % (expected_sig, str(actual_sig))) for (e, ev), (a, av) in zip(expected_sig, actual_sig.parameters.items()): assert e == a and ev == av.default def test_tabulate_signature(): "API: tabulate() type signature is unchanged""" assert type(tabulate) is type(lambda: None) expected_sig = [("tabular_data", _empty), ("headers", ()), ("tablefmt", "simple"), ("floatfmt", "g"), ("numalign", "decimal"), ("stralign", "left"), ("missingval", "")] _check_signature(tabulate, expected_sig) def test_simple_separated_format_signature(): "API: simple_separated_format() type signature is unchanged""" assert type(simple_separated_format) is type(lambda: None) expected_sig = [("separator", _empty)] _check_signature(simple_separated_format, expected_sig) tabulate-0.7.7/test/test_cli.py0000666000000000000000000001530312674206070014625 0ustar 00000000000000"""Command-line interface. """ from __future__ import print_function from __future__ import unicode_literals import os import subprocess import tempfile from common import assert_equal SAMPLE_SIMPLE_FORMAT = "\n".join( ['----- ------ -------------', 'Sun 696000 1.9891e+09', 'Earth 6371 5973.6', 'Moon 1737 73.5', 'Mars 3390 641.85', '----- ------ -------------']) SAMPLE_SIMPLE_FORMAT_WITH_HEADERS = "\n".join( ['Planet Radius Mass', '-------- -------- -------------', 'Sun 696000 1.9891e+09', 'Earth 6371 5973.6', 'Moon 1737 73.5', 'Mars 3390 641.85']) SAMPLE_GRID_FORMAT_WITH_HEADERS = "\n".join( ['+----------+----------+---------------+', '| Planet | Radius | Mass |', '+==========+==========+===============+', '| Sun | 696000 | 1.9891e+09 |', '+----------+----------+---------------+', '| Earth | 6371 | 5973.6 |', '+----------+----------+---------------+', '| Moon | 1737 | 73.5 |', '+----------+----------+---------------+', '| Mars | 3390 | 641.85 |', '+----------+----------+---------------+']) SAMPLE_GRID_FORMAT_WITH_DOT1E_FLOATS = "\n".join( ['+-------+--------+---------+', '| Sun | 696000 | 2.0e+09 |', '+-------+--------+---------+', '| Earth | 6371 | 6.0e+03 |', '+-------+--------+---------+', '| Moon | 1737 | 7.4e+01 |', '+-------+--------+---------+', '| Mars | 3390 | 6.4e+02 |', '+-------+--------+---------+']) def sample_input(sep=' ', with_headers=False): headers = sep.join(['Planet', 'Radius', 'Mass']) rows = [sep.join(['Sun', '696000', '1.9891e9']), sep.join(['Earth', '6371', '5973.6']), sep.join(['Moon', '1737', '73.5']), sep.join(['Mars', '3390', '641.85'])] all_rows = ([headers] + rows) if with_headers else rows table = "\n".join(all_rows) return table def run_and_capture_stdout(cmd, input=None): x = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) input_buf = input.encode() if input else None out, err = x.communicate(input=input_buf) out = out.decode("utf-8") if x.returncode != 0: raise IOError(err) return out class TemporaryTextFile(object): def __init__(self): self.tmpfile = None def __enter__(self): self.tmpfile = tempfile.NamedTemporaryFile("w+", prefix="tabulate-test-tmp-", delete=False) return self.tmpfile def __exit__(self, exc_type, exc_val, exc_tb): if self.tmpfile: self.tmpfile.close() os.unlink(self.tmpfile.name) def test_script_from_stdin_to_stdout(): """Command line utility: read from stdin, print to stdout""" cmd = ["python", "tabulate.py"] out = run_and_capture_stdout(cmd, input=sample_input()) expected = SAMPLE_SIMPLE_FORMAT print("got: ",repr(out)) print("expected:",repr(expected)) assert_equal(out.splitlines(), expected.splitlines()) def test_script_from_file_to_stdout(): """Command line utility: read from file, print to stdout""" with TemporaryTextFile() as tmpfile: tmpfile.write(sample_input()) tmpfile.seek(0) cmd = ["python", "tabulate.py", tmpfile.name] out = run_and_capture_stdout(cmd) expected = SAMPLE_SIMPLE_FORMAT print("got: ",repr(out)) print("expected:",repr(expected)) assert_equal(out.splitlines(), expected.splitlines()) def test_script_from_file_to_file(): """Command line utility: read from file, write to file""" with TemporaryTextFile() as input_file: with TemporaryTextFile() as output_file: input_file.write(sample_input()) input_file.seek(0) cmd = ["python", "tabulate.py", "-o", output_file.name, input_file.name] out = run_and_capture_stdout(cmd) # check that nothing is printed to stdout expected = "" print("got: ", repr(out)) print("expected:", repr(expected)) assert_equal(out.splitlines(), expected.splitlines()) # check that the output was written to file output_file.seek(0) out = output_file.file.read() expected = SAMPLE_SIMPLE_FORMAT print("got: ", repr(out)) print("expected:", repr(expected)) assert_equal(out.splitlines(), expected.splitlines()) def test_script_header_option(): """Command line utility: -1, --header option""" for option in ["-1", "--header"]: cmd = ["python", "tabulate.py", option] raw_table = sample_input(with_headers=True) out = run_and_capture_stdout(cmd, input=raw_table) expected = SAMPLE_SIMPLE_FORMAT_WITH_HEADERS print(out) print("got: ",repr(out)) print("expected:",repr(expected)) assert_equal(out.splitlines(), expected.splitlines()) def test_script_sep_option(): """Command line utility: -s, --sep option""" for option in ["-s", "--sep"]: cmd = ["python", "tabulate.py", option, ","] raw_table = sample_input(sep=",") out = run_and_capture_stdout(cmd, input=raw_table) expected = SAMPLE_SIMPLE_FORMAT print("got: ",repr(out)) print("expected:",repr(expected)) assert_equal(out.splitlines(), expected.splitlines()) def test_script_floatfmt_option(): """Command line utility: -F, --float option""" for option in ["-F", "--float"]: cmd = ["python", "tabulate.py", option, ".1e", "--format", "grid"] raw_table = sample_input() out = run_and_capture_stdout(cmd, input=raw_table) expected = SAMPLE_GRID_FORMAT_WITH_DOT1E_FLOATS print("got: ",repr(out)) print("expected:",repr(expected)) assert_equal(out.splitlines(), expected.splitlines()) def test_script_format_option(): """Command line utility: -f, --format option""" for option in ["-f", "--format"]: cmd = ["python", "tabulate.py", "-1", option, "grid"] raw_table = sample_input(with_headers=True) out = run_and_capture_stdout(cmd, input=raw_table) expected = SAMPLE_GRID_FORMAT_WITH_HEADERS print(out) print("got: ",repr(out)) print("expected:",repr(expected)) assert_equal(out.splitlines(), expected.splitlines()) tabulate-0.7.7/test/test_input.py0000666000000000000000000003551412674206070015223 0ustar 00000000000000# -*- coding: utf-8 -*- """Test support of the various forms of tabular data.""" from __future__ import print_function from __future__ import unicode_literals from tabulate import tabulate from common import assert_equal, assert_in, assert_raises, SkipTest def test_iterable_of_iterables(): "Input: an interable of iterables." ii = iter(map(lambda x: iter(x), [range(5), range(5,0,-1)])) expected = "\n".join( ['- - - - -', '0 1 2 3 4', '5 4 3 2 1', '- - - - -']) result = tabulate(ii) assert_equal(expected, result) def test_iterable_of_iterables_headers(): "Input: an interable of iterables with headers." ii = iter(map(lambda x: iter(x), [range(5), range(5,0,-1)])) expected = "\n".join( [' a b c d e', '--- --- --- --- ---', ' 0 1 2 3 4', ' 5 4 3 2 1']) result = tabulate(ii, "abcde") assert_equal(expected, result) def test_iterable_of_iterables_firstrow(): "Input: an interable of iterables with the first row as headers" ii = iter(map(lambda x: iter(x), ["abcde", range(5), range(5,0,-1)])) expected = "\n".join( [' a b c d e', '--- --- --- --- ---', ' 0 1 2 3 4', ' 5 4 3 2 1']) result = tabulate(ii, "firstrow") assert_equal(expected, result) def test_list_of_lists(): "Input: a list of lists with headers." ll = [["a","one",1],["b","two",None]] expected = "\n".join([ ' string number', '-- -------- --------', 'a one 1', 'b two']) result = tabulate(ll, headers=["string","number"]) assert_equal(expected, result) def test_list_of_lists_firstrow(): "Input: a list of lists with the first row as headers." ll = [["string","number"],["a","one",1],["b","two",None]] expected = "\n".join([ ' string number', '-- -------- --------', 'a one 1', 'b two']) result = tabulate(ll, headers="firstrow") assert_equal(expected, result) def test_list_of_lists_keys(): "Input: a list of lists with column indices as headers." ll = [["a","one",1],["b","two",None]] expected = "\n".join([ '0 1 2', '--- --- ---', 'a one 1', 'b two']) result = tabulate(ll, headers="keys") assert_equal(expected, result) def test_dict_like(): "Input: a dict of iterables with keys as headers." # columns should be padded with None, keys should be used as headers dd = {"a": range(3), "b": range(101,105)} # keys' order (hence columns' order) is not deterministic in Python 3 # => we have to consider both possible results as valid expected1 = "\n".join([ ' a b', '--- ---', ' 0 101', ' 1 102', ' 2 103', ' 104']) expected2 = "\n".join([ ' b a', '--- ---', '101 0', '102 1', '103 2', '104']) result = tabulate(dd, "keys") print("Keys' order: %s" % dd.keys()) assert_in(result, [expected1, expected2]) def test_numpy_2d(): "Input: a 2D NumPy array with headers." try: import numpy na = (numpy.arange(1,10, dtype=numpy.float32).reshape((3,3))**3)*0.5 expected = "\n".join([ ' a b c', '----- ----- -----', ' 0.5 4 13.5', ' 32 62.5 108', '171.5 256 364.5']) result = tabulate(na, ["a", "b", "c"]) assert_equal(expected, result) except ImportError: print("test_numpy_2d is skipped") raise SkipTest() # this test is optional def test_numpy_2d_firstrow(): "Input: a 2D NumPy array with the first row as headers." try: import numpy na = (numpy.arange(1,10, dtype=numpy.int32).reshape((3,3))**3) expected = "\n".join([ ' 1 8 27', '--- --- ----', ' 64 125 216', '343 512 729']) result = tabulate(na, headers="firstrow") assert_equal(expected, result) except ImportError: print("test_numpy_2d_firstrow is skipped") raise SkipTest() # this test is optional def test_numpy_2d_keys(): "Input: a 2D NumPy array with column indices as headers." try: import numpy na = (numpy.arange(1,10, dtype=numpy.float32).reshape((3,3))**3)*0.5 expected = "\n".join([ ' 0 1 2', '----- ----- -----', ' 0.5 4 13.5', ' 32 62.5 108', '171.5 256 364.5']) result = tabulate(na, headers="keys") assert_equal(expected, result) except ImportError: print("test_numpy_2d_keys is skipped") raise SkipTest() # this test is optional def test_numpy_record_array(): "Input: a 2D NumPy record array without header." try: import numpy na = numpy.asarray([("Alice", 23, 169.5), ("Bob", 27, 175.0)], dtype={"names":["name","age","height"], "formats":["a32","uint8","float32"]}) expected = "\n".join([ "----- -- -----", "Alice 23 169.5", "Bob 27 175", "----- -- -----" ]) result = tabulate(na) assert_equal(expected, result) except ImportError: print("test_numpy_2d_keys is skipped") raise SkipTest() # this test is optional def test_numpy_record_array_keys(): "Input: a 2D NumPy record array with column names as headers." try: import numpy na = numpy.asarray([("Alice", 23, 169.5), ("Bob", 27, 175.0)], dtype={"names":["name","age","height"], "formats":["a32","uint8","float32"]}) expected = "\n".join([ "name age height", "------ ----- --------", "Alice 23 169.5", "Bob 27 175" ]) result = tabulate(na, headers="keys") assert_equal(expected, result) except ImportError: print("test_numpy_2d_keys is skipped") raise SkipTest() # this test is optional def test_numpy_record_array_headers(): "Input: a 2D NumPy record array with user-supplied headers." try: import numpy na = numpy.asarray([("Alice", 23, 169.5), ("Bob", 27, 175.0)], dtype={"names":["name","age","height"], "formats":["a32","uint8","float32"]}) expected = "\n".join([ "person years cm", "-------- ------- -----", "Alice 23 169.5", "Bob 27 175" ]) result = tabulate(na, headers=["person", "years", "cm"]) assert_equal(expected, result) except ImportError: print("test_numpy_2d_keys is skipped") raise SkipTest() # this test is optional def test_pandas(): "Input: a Pandas DataFrame." try: import pandas df = pandas.DataFrame([["one",1],["two",None]], index=["a","b"]) expected = "\n".join([ ' string number', '-- -------- --------', 'a one 1', 'b two nan']) result = tabulate(df, headers=["string", "number"]) assert_equal(expected, result) except ImportError: print("test_pandas is skipped") raise SkipTest() # this test is optional def test_pandas_firstrow(): "Input: a Pandas DataFrame with the first row as headers." try: import pandas df = pandas.DataFrame([["one",1],["two",None]], columns=["string","number"], index=["a","b"]) expected = "\n".join([ 'a one 1.0', '--- ----- -----', 'b two nan']) result = tabulate(df, headers="firstrow") assert_equal(expected, result) except ImportError: print("test_pandas_firstrow is skipped") raise SkipTest() # this test is optional def test_pandas_keys(): "Input: a Pandas DataFrame with keys as headers." try: import pandas df = pandas.DataFrame([["one",1],["two",None]], columns=["string","number"], index=["a","b"]) expected = "\n".join( [' string number', '-- -------- --------', 'a one 1', 'b two nan']) result = tabulate(df, headers="keys") assert_equal(expected, result) except ImportError: print("test_pandas_keys is skipped") raise SkipTest() # this test is optional def test_sqlite3(): "Input: an sqlite3 cursor" try: import sqlite3 conn = sqlite3.connect(':memory:') cursor = conn.cursor() cursor.execute('CREATE TABLE people (name, age, height)') for values in [ ("Alice", 23, 169.5), ("Bob", 27, 175.0)]: cursor.execute('INSERT INTO people VALUES (?, ?, ?)', values) cursor.execute('SELECT name, age, height FROM people ORDER BY name') result = tabulate(cursor, headers=["whom", "how old", "how tall"]) expected = """\ whom how old how tall ------ --------- ---------- Alice 23 169.5 Bob 27 175""" assert_equal(expected, result) except ImportError: print("test_sqlite3 is skipped") raise SkipTest() # this test is optional def test_sqlite3_keys(): "Input: an sqlite3 cursor with keys as headers" try: import sqlite3 conn = sqlite3.connect(':memory:') cursor = conn.cursor() cursor.execute('CREATE TABLE people (name, age, height)') for values in [ ("Alice", 23, 169.5), ("Bob", 27, 175.0)]: cursor.execute('INSERT INTO people VALUES (?, ?, ?)', values) cursor.execute('SELECT name "whom", age "how old", height "how tall" FROM people ORDER BY name') result = tabulate(cursor, headers="keys") expected = """\ whom how old how tall ------ --------- ---------- Alice 23 169.5 Bob 27 175""" assert_equal(expected, result) except ImportError: print("test_sqlite3_keys is skipped") raise SkipTest() # this test is optional def test_list_of_namedtuples(): "Input: a list of named tuples with field names as headers." from collections import namedtuple NT = namedtuple("NT", ['foo', 'bar']) lt = [NT(1,2), NT(3,4)] expected = "\n".join([ '- -', '1 2', '3 4', '- -']) result = tabulate(lt) assert_equal(expected, result) def test_list_of_namedtuples_keys(): "Input: a list of named tuples with field names as headers." from collections import namedtuple NT = namedtuple("NT", ['foo', 'bar']) lt = [NT(1,2), NT(3,4)] expected = "\n".join([ ' foo bar', '----- -----', ' 1 2', ' 3 4']) result = tabulate(lt, headers="keys") assert_equal(expected, result) def test_list_of_dicts(): "Input: a list of dictionaries." lod = [{'foo' : 1, 'bar' : 2}, {'foo' : 3, 'bar' : 4}] expected1 = "\n".join([ '- -', '1 2', '3 4', '- -']) expected2 = "\n".join([ '- -', '2 1', '4 3', '- -']) result = tabulate(lod) assert_in(result, [expected1, expected2]) def test_list_of_dicts_keys(): "Input: a list of dictionaries, with keys as headers." lod = [{'foo' : 1, 'bar' : 2}, {'foo' : 3, 'bar' : 4}] expected1 = "\n".join([ ' foo bar', '----- -----', ' 1 2', ' 3 4']) expected2 = "\n".join([ ' bar foo', '----- -----', ' 2 1', ' 4 3']) result = tabulate(lod, headers="keys") assert_in(result, [expected1, expected2]) def test_list_of_dicts_with_missing_keys(): "Input: a list of dictionaries, with missing keys." lod = [{"foo": 1}, {"bar": 2}, {"foo":4, "baz": 3}] expected = "\n".join([ ' foo bar baz', '----- ----- -----', ' 1', ' 2', ' 4 3']) result = tabulate(lod, headers="keys") assert_equal(expected, result) def test_list_of_dicts_firstrow(): "Input: a list of dictionaries, with the first dict as headers." lod = [{'foo' : "FOO", 'bar' : "BAR"}, {'foo' : 3, 'bar': 4, 'baz': 5}] # if some key is missing in the first dict, use the key name instead expected1 = "\n".join([ ' FOO BAR baz', '----- ----- -----', ' 3 4 5']) expected2 = "\n".join([ ' BAR FOO baz', '----- ----- -----', ' 4 3 5']) result = tabulate(lod, headers="firstrow") assert_in(result, [expected1, expected2]) def test_list_of_dicts_with_dict_of_headers(): "Input: a dict of user headers for a list of dicts (issue #23)" table = [{"letters": "ABCDE", "digits": 12345}] headers = {"digits": "DIGITS", "letters": "LETTERS"} expected1 = "\n".join([ ' DIGITS LETTERS', '-------- ---------', ' 12345 ABCDE']) expected2 = "\n".join([ 'LETTERS DIGITS', '--------- --------', 'ABCDE 12345']) result = tabulate(table, headers=headers) assert_in(result, [expected1, expected2]) def test_list_of_dicts_with_list_of_headers(): "Input: ValueError on a list of headers with a list of dicts (issue #23)" table = [{"letters": "ABCDE", "digits": 12345}] headers = ["DIGITS", "LETTERS"] with assert_raises(ValueError): tabulate(table, headers=headers) def test_py27orlater_list_of_ordereddicts(): "Input: a list of OrderedDicts." from collections import OrderedDict od = OrderedDict([('b', 1), ('a', 2)]) lod = [od, od] expected = "\n".join([ ' b a', '--- ---', ' 1 2', ' 1 2']) result = tabulate(lod, headers="keys") assert_equal(expected, result) tabulate-0.7.7/test/test_output.py0000666000000000000000000005377013010106222015407 0ustar 00000000000000# -*- coding: utf-8 -*- """Test output of the various forms of tabular data.""" from __future__ import print_function from __future__ import unicode_literals from tabulate import tabulate, simple_separated_format from common import assert_equal, assert_raises, SkipTest # _test_table shows # - coercion of a string to a number, # - left alignment of text, # - decimal point alignment of numbers _test_table = [["spam", 41.9999], ["eggs", "451.0"]] _test_table_headers = ["strings", "numbers"] def test_plain(): "Output: plain with headers" expected = "\n".join(['strings numbers', 'spam 41.9999', 'eggs 451',]) result = tabulate(_test_table, _test_table_headers, tablefmt="plain") assert_equal(expected, result) def test_plain_headerless(): "Output: plain without headers" expected = "\n".join(['spam 41.9999', 'eggs 451',]) result = tabulate(_test_table, tablefmt="plain") assert_equal(expected, result) def test_simple(): "Output: simple with headers" expected = "\n".join(['strings numbers', '--------- ---------', 'spam 41.9999', 'eggs 451',]) result = tabulate(_test_table, _test_table_headers, tablefmt="simple") assert_equal(expected, result) def test_simple_headerless(): "Output: simple without headers" expected = "\n".join(['---- --------', 'spam 41.9999', 'eggs 451', '---- --------',]) result = tabulate(_test_table, tablefmt="simple") assert_equal(expected, result) def test_grid(): "Output: grid with headers" expected = '\n'.join(['+-----------+-----------+', '| strings | numbers |', '+===========+===========+', '| spam | 41.9999 |', '+-----------+-----------+', '| eggs | 451 |', '+-----------+-----------+',]) result = tabulate(_test_table, _test_table_headers, tablefmt="grid") assert_equal(expected, result) def test_grid_headerless(): "Output: grid without headers" expected = '\n'.join(['+------+----------+', '| spam | 41.9999 |', '+------+----------+', '| eggs | 451 |', '+------+----------+',]) result = tabulate(_test_table, tablefmt="grid") assert_equal(expected, result) def test_fancy_grid(): "Output: fancy_grid with headers" expected = '\n'.join([ '╒═══════════╤═══════════╕', '│ strings │ numbers │', '╞═══════════╪═══════════╡', '│ spam │ 41.9999 │', '├───────────┼───────────┤', '│ eggs │ 451 │', '╘═══════════╧═══════════╛',]) result = tabulate(_test_table, _test_table_headers, tablefmt="fancy_grid") assert_equal(expected, result) def test_fancy_grid_headerless(): "Output: fancy_grid without headers" expected = '\n'.join([ '╒══════╤══════════╕', '│ spam │ 41.9999 │', '├──────┼──────────┤', '│ eggs │ 451 │', '╘══════╧══════════╛',]) result = tabulate(_test_table, tablefmt="fancy_grid") assert_equal(expected, result) def test_pipe(): "Output: pipe with headers" expected = '\n'.join(['| strings | numbers |', '|:----------|----------:|', '| spam | 41.9999 |', '| eggs | 451 |',]) result = tabulate(_test_table, _test_table_headers, tablefmt="pipe") assert_equal(expected, result) def test_pipe_headerless(): "Output: pipe without headers" expected = '\n'.join(['|:-----|---------:|', '| spam | 41.9999 |', '| eggs | 451 |',]) result = tabulate(_test_table, tablefmt="pipe") assert_equal(expected, result) def test_orgtbl(): "Output: orgtbl with headers" expected = '\n'.join(['| strings | numbers |', '|-----------+-----------|', '| spam | 41.9999 |', '| eggs | 451 |',]) result = tabulate(_test_table, _test_table_headers, tablefmt="orgtbl") assert_equal(expected, result) def test_orgtbl_headerless(): "Output: orgtbl without headers" expected = '\n'.join(['| spam | 41.9999 |', '| eggs | 451 |',]) result = tabulate(_test_table, tablefmt="orgtbl") assert_equal(expected, result) def test_psql(): "Output: psql with headers" expected = '\n'.join(['+-----------+-----------+', '| strings | numbers |', '|-----------+-----------|', '| spam | 41.9999 |', '| eggs | 451 |', '+-----------+-----------+',]) result = tabulate(_test_table, _test_table_headers, tablefmt="psql") assert_equal(expected, result) def test_psql_headerless(): "Output: psql without headers" expected = '\n'.join(['+------+----------+', '| spam | 41.9999 |', '| eggs | 451 |', '+------+----------+',]) result = tabulate(_test_table, tablefmt="psql") assert_equal(expected, result) def test_jira(): "Output: jira with headers" expected = '\n'.join(['|| strings || numbers ||', '| spam | 41.9999 |', '| eggs | 451 |',]) result = tabulate(_test_table, _test_table_headers, tablefmt="jira") assert_equal(expected, result) def test_jira_headerless(): "Output: jira without headers" expected = '\n'.join(['| spam | 41.9999 |', '| eggs | 451 |',]) result = tabulate(_test_table, tablefmt="jira") assert_equal(expected, result) def test_rst(): "Output: rst with headers" expected = '\n'.join(['========= =========', 'strings numbers', '========= =========', 'spam 41.9999', 'eggs 451', '========= =========',]) result = tabulate(_test_table, _test_table_headers, tablefmt="rst") assert_equal(expected, result) def test_rst_headerless(): "Output: rst without headers" expected = '\n'.join(['==== ========', 'spam 41.9999', 'eggs 451', '==== ========',]) result = tabulate(_test_table, tablefmt="rst") assert_equal(expected, result) def test_mediawiki(): "Output: mediawiki with headers" expected = '\n'.join(['{| class="wikitable" style="text-align: left;"', '|+ ', '|-', '! strings !! align="right"| numbers', '|-', '| spam || align="right"| 41.9999', '|-', '| eggs || align="right"| 451', '|}',]) result = tabulate(_test_table, _test_table_headers, tablefmt="mediawiki") assert_equal(expected, result) def test_mediawiki_headerless(): "Output: mediawiki without headers" expected = '\n'.join(['{| class="wikitable" style="text-align: left;"', '|+ ', '|-', '| spam || align="right"| 41.9999', '|-', '| eggs || align="right"| 451', '|}',]) result = tabulate(_test_table, tablefmt="mediawiki") assert_equal(expected, result) def test_moinmoin(): "Output: moinmoin with headers" expected = "\n".join(['|| \'\'\' strings \'\'\' || \'\'\' numbers \'\'\' ||', '|| spam || 41.9999 ||', '|| eggs || 451 ||',]) result = tabulate(_test_table, _test_table_headers, tablefmt="moinmoin") assert_equal(expected, result) def test_moinmoin_headerless(): "Output: moinmoin without headers" expected = "\n".join(['|| spam || 41.9999 ||', '|| eggs || 451 ||',]) result = tabulate (_test_table, tablefmt="moinmoin") assert_equal(expected, result) def test_html(): "Output: html with headers" expected = '\n'.join([ '', '', '', '', '', '', '', '', '
strings numbers
spam 41.9999
eggs 451
',]) result = tabulate(_test_table, _test_table_headers, tablefmt="html") assert_equal(expected, result) def test_html_headerless(): "Output: html without headers" expected = '\n'.join([ '', '', '', '', '', '
spam 41.9999
eggs451
',]) result = tabulate(_test_table, tablefmt="html") assert_equal(expected, result) def test_latex(): "Output: latex with headers" result = tabulate(_test_table, _test_table_headers, tablefmt="latex") expected = "\n".join([r"\begin{tabular}{lr}", r"\hline", r" strings & numbers \\", r"\hline", r" spam & 41.9999 \\", r" eggs & 451 \\", r"\hline", r"\end{tabular}"]) assert_equal(expected, result) def test_latex_headerless(): "Output: latex without headers" result = tabulate(_test_table, tablefmt="latex") expected = "\n".join([r"\begin{tabular}{lr}", r"\hline", r" spam & 41.9999 \\", r" eggs & 451 \\", r"\hline", r"\end{tabular}"]) assert_equal(expected, result) def test_latex_booktabs(): "Output: latex with headers, using the booktabs format" result = tabulate(_test_table, _test_table_headers, tablefmt="latex_booktabs") expected = "\n".join([r"\begin{tabular}{lr}", r"\toprule", r" strings & numbers \\", r"\midrule", r" spam & 41.9999 \\", r" eggs & 451 \\", r"\bottomrule", r"\end{tabular}"]) assert_equal(expected, result) def test_latex_booktabs_headerless(): "Output: latex without headers, using the booktabs format" result = tabulate(_test_table, tablefmt="latex_booktabs") expected = "\n".join([r"\begin{tabular}{lr}", r"\toprule", r" spam & 41.9999 \\", r" eggs & 451 \\", r"\bottomrule", r"\end{tabular}"]) assert_equal(expected, result) def test_textile(): "Output: textile without header" result = tabulate(_test_table, tablefmt="textile") expected = """\ |<. spam |>. 41.9999 | |<. eggs |>. 451 |""" assert_equal(expected, result) def test_textile_with_header(): "Output: textile with header" result = tabulate(_test_table, ['strings', 'numbers'], tablefmt="textile") expected = """\ |_. strings |_. numbers | |<. spam |>. 41.9999 | |<. eggs |>. 451 |""" assert_equal(expected, result) def test_textile_with_center_align(): "Output: textile with center align" result = tabulate(_test_table, tablefmt="textile", stralign='center') expected = """\ |=. spam |>. 41.9999 | |=. eggs |>. 451 |""" assert_equal(expected, result) def test_no_data(): "Output: table with no data" expected = "\n".join(['strings numbers', '--------- ---------']) result = tabulate(None, _test_table_headers, tablefmt="simple") assert_equal(expected, result) def test_empty_data(): "Output: table with empty data" expected = "\n".join(['strings numbers', '--------- ---------']) result = tabulate([], _test_table_headers, tablefmt="simple") assert_equal(expected, result) def test_no_data_without_headers(): "Output: table with no data and no headers" expected = "" result = tabulate(None, tablefmt="simple") assert_equal(expected, result) def test_empty_data_without_headers(): "Output: table with empty data and no headers" expected = "" result = tabulate([], tablefmt="simple") assert_equal(expected, result) def test_floatfmt(): "Output: floating point format" result = tabulate([['1.23456789'],[1.0]], floatfmt=".3f", tablefmt="plain") expected = '1.235\n1.000' assert_equal(expected, result) def test_missingval(): "Output: substitution of missing values" result = tabulate([['Alice', 10],['Bob', None]], missingval="n/a", tablefmt="plain") expected = 'Alice 10\nBob n/a' assert_equal(expected, result) def test_column_alignment(): "Output: custom alignment for text and numbers" expected = '\n'.join(['----- ---', 'Alice 1', ' Bob 333', '----- ---',]) result = tabulate([['Alice', 1],['Bob', 333]], stralign="right", numalign="center") assert_equal(expected, result) def test_unaligned_separated(): "Output: non-aligned data columns" expected = '\n'.join(['name|score', 'Alice|1', 'Bob|333']) fmt = simple_separated_format("|") result = tabulate([['Alice', 1],['Bob', 333]], ["name", "score"], tablefmt=fmt, stralign=None, numalign=None) assert_equal(expected, result) def test_pandas_with_index(): "Output: a pandas Dataframe with an index" try: import pandas df = pandas.DataFrame([["one",1],["two",None]], columns=["string","number"], index=["a","b"]) expected = "\n".join( [' string number', '-- -------- --------', 'a one 1', 'b two nan']) result = tabulate(df, headers="keys") assert_equal(expected, result) except ImportError: print("test_pandas_with_index is skipped") raise SkipTest() # this test is optional def test_pandas_without_index(): "Output: a pandas Dataframe without an index" try: import pandas df = pandas.DataFrame([["one",1],["two",None]], columns=["string","number"], index=["a","b"]) expected = "\n".join( ['string number', '-------- --------', 'one 1', 'two nan']) result = tabulate(df, headers="keys", showindex=False) assert_equal(expected, result) except ImportError: print("test_pandas_without_index is skipped") raise SkipTest() # this test is optional def test_pandas_rst_with_index(): "Output: a pandas Dataframe with an index in ReStructuredText format" try: import pandas df = pandas.DataFrame([["one", 1], ["two", None]], columns=["string", "number"], index=["a", "b"]) expected = "\n".join( ['==== ======== ========', '.. string number', '==== ======== ========', 'a one 1', 'b two nan', '==== ======== ========']) result = tabulate(df, tablefmt="rst", headers="keys") assert_equal(expected, result) except ImportError: print("test_pandas_rst_with_index is skipped") raise SkipTest() # this test is optional def test_pandas_rst_with_named_index(): "Output: a pandas Dataframe with a named index in ReStructuredText format" try: import pandas index = pandas.Index(["a", "b"], name='index') df = pandas.DataFrame([["one", 1], ["two", None]], columns=["string", "number"], index=index) expected = "\n".join( ['======= ======== ========', 'index string number', '======= ======== ========', 'a one 1', 'b two nan', '======= ======== ========']) result = tabulate(df, tablefmt="rst", headers="keys") assert_equal(expected, result) except ImportError: print("test_pandas_rst_with_index is skipped") raise SkipTest() # this test is optional def test_dict_like_with_index(): "Output: a table with a running index" dd = {"b": range(101,104)} expected = "\n".join([ ' b', '-- ---', ' 0 101', ' 1 102', ' 2 103']) result = tabulate(dd, "keys", showindex=True) assert_equal(result, expected) def test_list_of_lists_with_index(): "Output: a table with a running index" dd = zip(*[range(3), range(101,104)]) # keys' order (hence columns' order) is not deterministic in Python 3 # => we have to consider both possible results as valid expected = "\n".join([ ' a b', '-- --- ---', ' 0 0 101', ' 1 1 102', ' 2 2 103']) result = tabulate(dd, headers=["a","b"], showindex=True) assert_equal(result, expected) def test_list_of_lists_with_supplied_index(): "Output: a table with a supplied index" dd = zip(*[list(range(3)), list(range(101,104))]) expected = "\n".join([ ' a b', '-- --- ---', ' 1 0 101', ' 2 1 102', ' 3 2 103']) result = tabulate(dd, headers=["a","b"], showindex=[1,2,3]) assert_equal(result, expected) # TODO: make it a separate test case # the index must be as long as the number of rows assert_raises(ValueError, lambda: tabulate(dd, headers=["a","b"], showindex=[1,2])) def test_list_of_lists_with_index_firstrow(): "Output: a table with a running index and header='firstrow'" dd = zip(*[["a"]+list(range(3)), ["b"]+list(range(101,104))]) expected = "\n".join([ ' a b', '-- --- ---', ' 0 0 101', ' 1 1 102', ' 2 2 103']) result = tabulate(dd, headers="firstrow", showindex=True) assert_equal(result, expected) # TODO: make it a separate test case # the index must be as long as the number of rows assert_raises(ValueError, lambda: tabulate(dd, headers="firstrow", showindex=[1,2])) def test_disable_numparse_default(): "Output: Default table output with number parsing and alignment" expected = "\n".join(['strings numbers', '--------- ---------', 'spam 41.9999', 'eggs 451',]) result = tabulate(_test_table, _test_table_headers) assert_equal(expected, result) result = tabulate(_test_table, _test_table_headers, disable_numparse=False) assert_equal(expected, result) def test_disable_numparse_true(): "Output: Default table output, but without number parsing and alignment" expected = "\n".join(['strings numbers', '--------- ---------', 'spam 41.9999', 'eggs 451.0',]) result = tabulate(_test_table, _test_table_headers, disable_numparse=True) assert_equal(expected, result) def test_disable_numparse_list(): "Output: Default table output, but with number parsing selectively disabled" table_headers = ['h1', 'h2', 'h3'] test_table = [['foo', 'bar', '42992e1']] expected = "\n".join(['h1 h2 h3', '---- ---- -------', 'foo bar 42992e1',]) result = tabulate(test_table, table_headers, disable_numparse=[2]) assert_equal(expected, result) expected = "\n".join(['h1 h2 h3', '---- ---- ------', 'foo bar 429920',]) result = tabulate(test_table, table_headers, disable_numparse=[0, 1]) assert_equal(expected, result) tabulate-0.7.7/test/test_regression.py0000666000000000000000000002731213010106222016220 0ustar 00000000000000# -*- coding: utf-8 -*- """Regression tests.""" from __future__ import print_function from __future__ import unicode_literals from tabulate import tabulate, _text_type, _long_type from common import assert_equal, assert_in, SkipTest def test_ansi_color_in_table_cells(): "Regression: ANSI color in table cells (issue #5)." colortable = [('test', '\x1b[31mtest\x1b[0m', '\x1b[32mtest\x1b[0m')] colorlessheaders = ('test', 'test', 'test') formatted = tabulate(colortable, colorlessheaders, 'pipe') expected = "\n".join(['| test | test | test |', '|:-------|:-------|:-------|', '| test | \x1b[31mtest\x1b[0m | \x1b[32mtest\x1b[0m |']) print("expected: %r\n\ngot: %r\n" % (expected, formatted)) assert_equal(expected, formatted) def test_alignment_of_colored_cells(): "Regression: Align ANSI-colored values as if they were colorless." colortable = [('test', 42, '\x1b[31m42\x1b[0m'), ('test', 101, '\x1b[32m101\x1b[0m')] colorheaders = ('test', '\x1b[34mtest\x1b[0m', 'test') formatted = tabulate(colortable, colorheaders, 'grid') expected = '\n'.join(['+--------+--------+--------+', '| test | \x1b[34mtest\x1b[0m | test |', '+========+========+========+', '| test | 42 | \x1b[31m42\x1b[0m |', '+--------+--------+--------+', '| test | 101 | \x1b[32m101\x1b[0m |', '+--------+--------+--------+']) print("expected: %r\n\ngot: %r\n" % (expected, formatted)) assert_equal(expected, formatted) def test_iter_of_iters_with_headers(): "Regression: Generator of generators with a gen. of headers (issue #9)." def mk_iter_of_iters(): def mk_iter(): for i in range(3): yield i for r in range(3): yield mk_iter() def mk_headers(): for h in ["a", "b", "c"]: yield h formatted = tabulate(mk_iter_of_iters(), headers=mk_headers()) expected = '\n'.join([' a b c', '--- --- ---', ' 0 1 2', ' 0 1 2', ' 0 1 2']) print("expected: %r\n\ngot: %r\n" % (expected, formatted)) assert_equal(expected, formatted) def test_datetime_values(): "Regression: datetime, date, and time values in cells (issue #10)." import datetime dt = datetime.datetime(1991,2,19,17,35,26) d = datetime.date(1991,2,19) t = datetime.time(17,35,26) formatted = tabulate([[dt, d, t]]) expected = '\n'.join(['------------------- ---------- --------', '1991-02-19 17:35:26 1991-02-19 17:35:26', '------------------- ---------- --------']) print("expected: %r\n\ngot: %r\n" % (expected, formatted)) assert_equal(expected, formatted) def test_simple_separated_format(): "Regression: simple_separated_format() accepts any separator (issue #12)" from tabulate import simple_separated_format fmt = simple_separated_format("!") expected = 'spam!eggs' formatted = tabulate([["spam", "eggs"]], tablefmt=fmt) print("expected: %r\n\ngot: %r\n" % (expected, formatted)) assert_equal(expected, formatted) def py3test_require_py3(): "Regression: py33 tests should actually use Python 3 (issue #13)" from platform import python_version_tuple print("Expected Python version: 3.x.x") print("Python version used for tests: %s.%s.%s" % python_version_tuple()) assert_equal(python_version_tuple()[0], '3') def test_simple_separated_format_with_headers(): "Regression: simple_separated_format() on tables with headers (issue #15)" from tabulate import simple_separated_format expected = ' a| b\n 1| 2' formatted = tabulate([[1,2]], headers=["a", "b"], tablefmt=simple_separated_format("|")) assert_equal(expected, formatted) def test_column_type_of_bytestring_columns(): "Regression: column type for columns of bytestrings (issue #16)" from tabulate import _column_type, _binary_type result = _column_type([b"foo", b"bar"]) expected = _binary_type assert_equal(result, expected) def test_numeric_column_headers(): "Regression: numbers as column headers (issue #22)" result = tabulate([[1],[2]], [42]) expected = ' 42\n----\n 1\n 2' assert_equal(result, expected) lod = [dict((p,i) for p in range(5)) for i in range(5)] result = tabulate(lod, "keys") expected = "\n".join([ " 0 1 2 3 4", "--- --- --- --- ---", " 0 0 0 0 0", " 1 1 1 1 1", " 2 2 2 2 2", " 3 3 3 3 3", " 4 4 4 4 4",]) assert_equal(result, expected) def test_88_256_ANSI_color_codes(): "Regression: color codes for terminals with 88/256 colors (issue #26)" colortable = [('\x1b[48;5;196mred\x1b[49m', '\x1b[38;5;196mred\x1b[39m')] colorlessheaders = ('background', 'foreground') formatted = tabulate(colortable, colorlessheaders, 'pipe') expected = "\n".join([ '| background | foreground |', '|:-------------|:-------------|', '| \x1b[48;5;196mred\x1b[49m | \x1b[38;5;196mred\x1b[39m |']) print("expected: %r\n\ngot: %r\n" % (expected, formatted)) assert_equal(expected, formatted) def test_column_with_mixed_value_types(): "Regression: mixed value types in the same column (issue #31)" expected = '\n'.join([ '-----', '', 'a', 'я', '0', 'False', '-----', ]) data = [[None], ['a'], ['\u044f'], [0], [False]] table = tabulate(data) assert_equal(table, expected) def test_latex_escape_special_chars(): "Regression: escape special characters in LaTeX output (issue #32)" expected = "\n".join([ r'\begin{tabular}{l}', r'\hline', r' foo\^{}bar \\', r'\hline', r' \&\%\^{}\_\$\#\{\}\ensuremath{<}\ensuremath{>}\textasciitilde{} \\', r'\hline', r'\end{tabular}']) result = tabulate([["&%^_$#{}<>~"]], ["foo^bar"], tablefmt="latex") assert_equal(result, expected) def test_isconvertible_on_set_values(): "Regression: don't fail with TypeError on set values (issue #35)" expected_py2 = "\n".join([ 'a b', '--- -------', 'Foo set([])',]) expected_py3 = "\n".join([ 'a b', '--- -----', 'Foo set()',]) result = tabulate([["Foo",set()]], headers=["a","b"]) assert_in(result, [expected_py2, expected_py3]) def test_ansi_color_for_decimal_numbers(): "Regression: ANSI colors for decimal numbers (issue #36)" table = [["Magenta", "\033[95m" + "1.1" + "\033[0m"]] expected = "\n".join([ '------- ---', 'Magenta \x1b[95m1.1\x1b[0m', '------- ---']) result = tabulate(table) assert_equal(result, expected) def test_alignment_of_decimal_numbers_with_ansi_color(): "Regression: alignment for decimal numbers with ANSI color (issue #42)" v1 = "\033[95m" + "12.34" + "\033[0m" v2 = "\033[95m" + "1.23456" + "\033[0m" table = [[v1], [v2]] expected = "\n".join([ '\x1b[95m12.34\x1b[0m', ' \x1b[95m1.23456\x1b[0m']) result = tabulate(table, tablefmt="plain") assert_equal(result, expected) def test_long_integers(): "Regression: long integers should be printed as integers (issue #48)" table = [[18446744073709551614]] result = tabulate(table, tablefmt="plain") expected = "18446744073709551614" assert_equal(result, expected) def test_colorclass_colors(): "Regression: ANSI colors in a unicode/str subclass (issue #49)" try: import colorclass s = colorclass.Color("{magenta}3.14{/magenta}") result = tabulate([[s]], tablefmt="plain") expected = "\x1b[35m3.14\x1b[39m" assert_equal(result, expected) except ImportError: class textclass(_text_type): pass s = textclass("\x1b[35m3.14\x1b[39m") result = tabulate([[s]], tablefmt="plain") expected = "\x1b[35m3.14\x1b[39m" assert_equal(result, expected) def test_mix_normal_and_wide_characters(): "Regression: wide characters in a grid format (issue #51)" try: import wcwidth ru_text = '\u043f\u0440\u0438\u0432\u0435\u0442' cn_text = '\u4f60\u597d' result = tabulate([[ru_text], [cn_text]], tablefmt="grid") expected = "\n".join([ '+--------+', '| \u043f\u0440\u0438\u0432\u0435\u0442 |', '+--------+', '| \u4f60\u597d |', '+--------+']) assert_equal(result, expected) except ImportError: print("test_mix_normal_and_wide_characters is skipped (requires wcwidth lib)") raise SkipTest() def test_align_long_integers(): "Regression: long integers should be aligned as integers (issue #61)" table = [[_long_type(1)], [_long_type(234)]] result = tabulate(table, tablefmt="plain") expected = "\n".join([" 1", "234"]) assert_equal(result, expected) def test_numpy_array_as_headers(): "Regression: NumPy array used as headers (issue #62)" try: import numpy as np headers = np.array(["foo", "bar"]) result = tabulate([], headers, tablefmt="plain") expected = "foo bar" assert_equal(result, expected) except ImportError: raise SkipTest() def test_boolean_columns(): "Regression: recognize boolean columns (issue #64)" xortable = [[False, True], [True, False]] expected = "\n".join(["False True", "True False"]) result = tabulate(xortable, tablefmt="plain") assert_equal(result, expected) def test_ansi_color_bold_and_fgcolor(): "Regression: set ANSI color and bold face together (issue #65)" table = [["1", "2", "3"], ["4", "\x1b[1;31m5\x1b[1;m", "6"], ["7", "8", "9"]] result = tabulate(table, tablefmt="grid") expected = "\n".join([ u'+---+---+---+', u'| 1 | 2 | 3 |', u'+---+---+---+', u'| 4 | \x1b[1;31m5\x1b[1;m | 6 |', u'+---+---+---+', u'| 7 | 8 | 9 |', u'+---+---+---+']) assert_equal(result, expected) def test_empty_table_with_keys_as_header(): "Regression: headers='keys' on an empty table (issue #81)" result = tabulate([], headers="keys") expected = "" assert_equal(result, expected) def test_escape_empty_cell_in_first_column_in_rst(): "Regression: escape empty cells of the first column in RST format (issue #82)" table = [["foo", 1], ["", 2], ["bar", 3]] headers = ["", "val"] expected = "\n".join([ u"==== =====", u".. val", u"==== =====", u"foo 1", u".. 2", u"bar 3", u"==== ====="]) result = tabulate(table, headers, tablefmt="rst") assert_equal(result, expected) def test_ragged_rows(): "Regression: allow rows with different number of columns (issue #85)" table = [[1,2,3], [1,2], [1,2,3,4]] expected = "\n".join([ u"- - - -", u"1 2 3", u"1 2", u"1 2 3 4", u"- - - -"]) result = tabulate(table) assert_equal(result, expected)