Commits

Sergey Astanin committed a83993e

first version (tabulates string values correctly; not ok on integers and exponents)

Comments (0)

Files changed (3)

+Copyright (c) 2011-2012 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.
+#!/usr/bin/env python
+
+from distutils.core import setup
+
+LICENSE = open("LICENSE").read()
+
+setup(name='tabulate',
+   version='0.1',
+   description='Pretty-print tabular data',
+   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",
+                  "Topic :: Software Development :: Libraries" ],
+   py_modules = ['tabulate'])
+"""Pretty-print tabular data."""
+
+__all__ = ["tabulate"]
+
+
+def _isnumber(string):
+    try:
+        n = float(string)
+        return True
+    except ValueError:
+        return False
+
+
+def _afterpoint(string):
+    "Symbols after a decimal point."
+    if _isnumber(string):
+        pos = string.rfind(".")
+        if pos >= 0:
+            return len(string) - pos - 1
+        else:
+            return 0  # no point
+    else:
+        return 0  # not a number
+
+
+def _format(v, numfmt, isnum):
+    "Format value if it is not a string."
+    if isinstance(v, basestring) or not isnum or not numfmt:
+        return unicode(v)
+    else:
+        return format(v, numfmt)
+
+
+def tabulate(list_of_lists, headers=[], colsep=" ", numfmt=None):
+    "Format a fixed width table for pretty printing."
+
+    # format rows and columns, convert numeric values to strings
+    cols = zip(*list_of_lists)
+    isnums = [ all(map(_isnumber, c)) for c in cols ]
+    cols = [ [ _format(v, numfmt, isnum) for v in c ]
+             for c, isnum in zip(cols, isnums) ]
+    rows = zip(*cols)
+
+    # calculate width of every column and decimal point position
+    widths = [ max(map(len, c)) for c in cols ]
+    if headers:
+        widths = [ max(w, len(h)) for w, h in zip(widths, headers) ]
+    pointpads = [ max(map(_afterpoint, c)) for c in cols ]
+
+    # add headers if necessary
+    lines = []
+    if headers:
+        ln = []
+        for s, width, isnum in zip(headers, widths, isnums):
+            if isnum:
+                ln.append(("{:>%ds}"%width).format(s))
+            else:
+                ln.append(("{:<%ds}"%width).format(s))
+        lines.append(colsep.join(ln))
+        lines.append(colsep.join([ '-'*w for w in widths ]))
+
+    # format table rows
+    for row in rows:
+        ln = []
+        for s, width, maxpad in zip(row, widths, pointpads):
+            if _isnumber(s):
+                rpad = " "*(maxpad-_afterpoint(s))
+                ln.append(("{:>%ds}"%width).format(s + rpad))
+            else:
+                ln.append(("{:<%ds}"%width).format(s))
+        lines.append(colsep.join(ln))
+    return "\n".join(lines)