Skip to content
Browse files

Initial import to Google code repository, take 2!

git-svn-id: http://prettytable.googlecode.com/svn/trunk@5 0f58610c-415a-11de-9c03-5d6cfad8e937
  • Loading branch information...
1 parent 938d3d0 commit 976de4dfe43944b7e270fb24d56a083d483cbbd5 luke.maurits committed May 26, 2009
Showing with 665 additions and 0 deletions.
  1. +27 −0 COPYING
  2. +625 −0 src/prettytable.py
  3. +13 −0 src/setup.py
View
27 COPYING
@@ -0,0 +1,27 @@
+# Copyright (c) 2009, Luke Maurits <luke@maurits.id.au>
+# All rights reserved.
+# With contributions from:
+# * Chris Clark
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * The name of the author may not be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
View
625 src/prettytable.py
@@ -0,0 +1,625 @@
+#!/usr/bin/env python
+#
+# PrettyTable TRUNK
+# Copyright (c) 2009, Luke Maurits <luke@maurits.id.au>
+# All rights reserved.
+# With contributions from:
+# * Chris Clark
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * The name of the author may not be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import cgi
+import copy
+import cPickle
+import sys
+
+FRAME = 0
+ALL = 1
+NONE = 2
+
+class PrettyTable:
+
+ def __init__(self, fields=None, caching=True, padding_width=1, left_padding=None, right_padding=None):
+
+ """Return a new PrettyTable instance
+
+ Arguments:
+
+ fields - list or tuple of field names
+ caching - boolean value to turn string caching on/off
+ padding width - number of spaces between column lines and content"""
+
+ # Data
+ self.fields = []
+ if fields:
+ self.set_field_names(fields)
+ else:
+ self.widths = []
+ self.aligns = []
+ self.set_padding_width(padding_width)
+ self.rows = []
+ self.cache = {}
+ self.html_cache = {}
+
+ # Options
+ self.hrules = FRAME
+ self.caching = caching
+ self.padding_width = padding_width
+ self.left_padding = left_padding
+ self.right_padding = right_padding
+ self.vertical_char = "|"
+ self.horizontal_char = "-"
+ self.junction_char = "+"
+
+ def __getslice__(self, i, j):
+
+ """Return a new PrettyTable whose data rows are a slice of this one's
+
+ Arguments:
+
+ i - beginning slice index
+ j - ending slice index"""
+
+ newtable = copy.deepcopy(self)
+ newtable.rows = self.rows[i:j]
+ return newtable
+
+ def __str__(self):
+
+ return self.get_string()
+
+ ##############################
+ # ATTRIBUTE SETTERS #
+ ##############################
+
+ def set_field_names(self, fields):
+
+ """Set the names of the fields
+
+ Arguments:
+
+ fields - list or tuple of field names"""
+
+ # We *may* need to change the widths if this isn't the first time
+ # setting the field names. This could certainly be done more
+ # efficiently.
+ if self.fields:
+ self.widths = [len(field) for field in fields]
+ for row in self.rows:
+ for i in range(0,len(row)):
+ if len(unicode(row[i])) > self.widths[i]:
+ self.widths[i] = len(unicode(row[i]))
+ else:
+ self.widths = [len(field) for field in fields]
+ self.fields = fields
+ self.aligns = len(fields)*["c"]
+ self.cache = {}
+ self.html_cache = {}
+
+ def set_field_align(self, fieldname, align):
+
+ """Set the alignment of a field by its fieldname
+
+ Arguments:
+
+ fieldname - name of the field whose alignment is to be changed
+ align - desired alignment - "l" for left, "c" for centre and "r" for right"""
+
+ if fieldname not in self.fields:
+ raise Exception("No field %s exists!" % fieldname)
+ if align not in ["l","c","r"]:
+ raise Exception("Alignment %s is invalid, use l, c or r!" % align)
+ self.aligns[self.fields.index(fieldname)] = align
+ self.cache = {}
+ self.html_cache = {}
+
+ def set_padding_width(self, padding_width):
+
+ """Set the number of empty spaces between a column's edge and its content
+
+ Arguments:
+
+ padding_width - number of spaces, must be a positive integer"""
+
+ try:
+ assert int(padding_width) >= 0
+ except AssertionError:
+ raise Exception("Invalid value for padding_width: %s!" % unicode(padding_width))
+
+ self.padding_width = padding_width
+ self.cache = {}
+ self.html_cache = {}
+
+ def set_left_padding(self, left_padding):
+
+ """Set the number of empty spaces between a column's left edge and its content
+
+ Arguments:
+
+ left_padding - number of spaces, must be a positive integer"""
+
+ try:
+ assert left_padding == None or int(left_padding) >= 0
+ except AssertionError:
+ raise Exception("Invalid value for left_padding: %s!" % unicode(left_padding))
+
+ self.left_padding = left_padding
+ self.cache = {}
+ self.html_cache = {}
+
+ def set_right_padding(self, right_padding):
+
+ """Set the number of empty spaces between a column's right edge and its content
+
+ Arguments:
+
+ right_padding - number of spaces, must be a positive integer"""
+
+ try:
+ assert right_padding == None or int(right_padding) >= 0
+ except AssertionError:
+ raise Exception("Invalid value for right_padding: %s!" % unicode(right_padding))
+
+ self.right_padding = right_padding
+ self.cache = {}
+ self.html_cache = {}
+
+ def set_border_chars(self, vertical="|", horizontal="-", junction="+"):
+
+ """Set the characters to use when drawing the table border
+
+ Arguments:
+
+ vertical - character used to draw a vertical line segment. Default is |
+ horizontal - character used to draw a horizontal line segment. Default is -
+ junction - character used to draw a line junction. Default is +"""
+
+ if len(vertical) > 1 or len(horizontal) > 1 or len(junction) > 1:
+ raise Exception("All border characters must be strings of length ONE!")
+ self.vertical_char = vertical
+ self.horizontal_char = horizontal
+ self.junction_char = junction
+ self.cache = {}
+
+ ##############################
+ # DATA INPUT METHODS #
+ ##############################
+
+ def add_row(self, row):
+
+ """Add a row to the table
+
+ Arguments:
+
+ row - row of data, should be a list with as many elements as the table
+ has fields"""
+
+ if len(row) != len(self.fields):
+ raise Exception("Row has incorrect number of values, (actual) %d!=%d (expected)" %(len(row),len(self.fields)))
+ self.rows.append(row)
+ for i in range(0,len(row)):
+ if len(unicode(row[i])) > self.widths[i]:
+ self.widths[i] = len(unicode(row[i]))
+ self.html_cache = {}
+
+ def add_column(self, fieldname, column, align="c"):
+
+ """Add a column to the table.
+
+ Arguments:
+
+ fieldname - name of the field to contain the new column of data
+ column - column of data, should be a list with as many elements as the
+ table has rows
+ align - desired alignment for this column - "l" for left, "c" for centre and "r" for right"""
+
+ if len(self.rows) in (0, len(column)):
+ if align not in ["l","c","r"]:
+ raise Exception("Alignment %s is invalid, use l, c or r!" % align)
+ self.fields.append(fieldname)
+ self.widths.append(len(fieldname))
+ self.aligns.append(align)
+ for i in range(0, len(column)):
+ if len(self.rows) < i+1:
+ self.rows.append([])
+ self.rows[i].append(column[i])
+ if len(unicode(column[i])) > self.widths[-1]:
+ self.widths[-1] = len(unicode(column[i]))
+ else:
+ raise Exception("Column length %d does not match number of rows %d!" % (len(column), len(self.rows)))
+
+ ##############################
+ # MISC PRIVATE METHODS #
+ ##############################
+
+ def _get_sorted_rows(self, start, end, sortby, reversesort):
+ # Sort rows using the "Decorate, Sort, Undecorate" (DSU) paradigm
+ rows = copy.deepcopy(self.rows[start:end])
+ sortindex = self.fields.index(sortby)
+ # Decorate
+ rows = [[row[sortindex]]+row for row in rows]
+ # Sort
+ rows.sort(reverse=reversesort)
+ # Undecorate
+ rows = [row[1:] for row in rows]
+ return rows
+
+ def _get_paddings(self):
+
+ if self.left_padding is not None:
+ lpad = self.left_padding
+ else:
+ lpad = self.padding_width
+ if self.right_padding is not None:
+ rpad = self.right_padding
+ else:
+ rpad = self.padding_width
+ return lpad, rpad
+
+ ##############################
+ # ASCII PRINT/STRING METHODS #
+ ##############################
+
+ def printt(self, start=0, end=None, fields=None, header=True, border=True, hrules=FRAME, sortby=None, reversesort=False):
+
+ """Print table in current state to stdout.
+
+ Arguments:
+
+ start - index of first data row to include in output
+ end - index of last data row to include in output PLUS ONE (list slice style)
+ fields - names of fields (columns) to include
+ sortby - name of field to sort rows by
+ reversesort - True or False to sort in descending or ascending order
+ border - should be True or False to print or not print borders
+ hrules - controls printing of horizontal rules after each row. Allowed values: FRAME, ALL, NONE"""
+
+ print self.get_string(start, end, fields, header, border, hrules, sortby, reversesort)
+
+ def get_string(self, start=0, end=None, fields=None, header=True, border=True, hrules=FRAME, sortby=None, reversesort=False):
+
+ """Return string representation of table in current state.
+
+ Arguments:
+
+ start - index of first data row to include in output
+ end - index of last data row to include in output PLUS ONE (list slice style)
+ fields - names of fields (columns) to include
+ sortby - name of field to sort rows by
+ reversesort - True or False to sort in descending or ascending order
+ border - should be True or False to print or not print borders
+ hrules - controls printing of horizontal rules after each row. Allowed values: FRAME, ALL, NONE"""
+
+ if self.caching:
+ key = cPickle.dumps((start, end, fields, header, border, hrules, sortby, reversesort))
+ if key in self.cache:
+ return self.cache[key]
+
+ hrule = hrules or self.hrules
+ bits = []
+ if not self.fields:
+ return ""
+ if not header:
+ # Recalculate widths - avoids tables with long field names but narrow data looking odd
+ old_widths = self.widths[:]
+ self.widths = [0]*len(self.fields)
+ for row in self.rows:
+ for i in range(0,len(row)):
+ if len(unicode(row[i])) > self.widths[i]:
+ self.widths[i] = len(unicode(row[i]))
+ if header:
+ bits.append(self._stringify_header(fields, border, hrules))
+ elif border and hrules != NONE:
+ bits.append(self._stringify_hrule(fields, border))
+ if sortby:
+ rows = self._get_sorted_rows(start, end, sortby, reversesort)
+ else:
+ rows = self.rows[start:end]
+ for row in rows:
+ bits.append(self._stringify_row(row, fields, border, hrule))
+ if border and not hrule:
+ bits.append(self._stringify_hrule(fields, border))
+ string = "\n".join(bits)
+
+ if self.caching:
+ self.cache[key] = string
+
+ if not header:
+ # Restore previous widths
+ self.widths = old_widths
+ for row in self.rows:
+ for i in range(0,len(row)):
+ if len(unicode(row[i])) > self.widths[i]:
+ self.widths[i] = len(unicode(row[i]))
+
+ return string
+
+ def _stringify_hrule(self, fields=None, border=True):
+
+ if not border:
+ return ""
+ lpad, rpad = self._get_paddings()
+ padding_width = lpad+rpad
+ bits = [self.junction_char]
+ for field, width in zip(self.fields, self.widths):
+ if fields and field not in fields:
+ continue
+ bits.append((width+padding_width)*self.horizontal_char)
+ bits.append(self.junction_char)
+ return "".join(bits)
+
+ def _stringify_header(self, fields=None, border=True, hrules=FRAME):
+
+ lpad, rpad = self._get_paddings()
+ bits = []
+ if border:
+ if hrules != NONE:
+ bits.append(self._stringify_hrule(fields, border))
+ bits.append("\n")
+ bits.append(self.vertical_char)
+ for field, width in zip(self.fields, self.widths):
+ if fields and field not in fields:
+ continue
+ bits.append(" " * lpad + field.center(width) + " " * rpad)
+ if border:
+ bits.append(self.vertical_char)
+ if border and hrules != NONE:
+ bits.append("\n")
+ bits.append(self._stringify_hrule(fields, border))
+ return "".join(bits)
+
+ def _stringify_row(self, row, fields=None, border=True, hrule=False):
+
+ lpad, rpad = self._get_paddings()
+ bits = []
+ if border:
+ bits.append(self.vertical_char)
+ for field, value, width, align in zip(self.fields, row, self.widths, self.aligns):
+ if fields and field not in fields:
+ continue
+ if align == "l":
+ bits.append(" " * lpad + unicode(value).ljust(width) + " " * rpad)
+ elif align == "r":
+ bits.append(" " * lpad + unicode(value).rjust(width) + " " * rpad)
+ else:
+ bits.append(" " * lpad + unicode(value).center(width) + " " * rpad)
+ if border:
+ bits.append(self.vertical_char)
+ if border and hrule == ALL:
+ bits.append("\n")
+ bits.append(self._stringify_hrule(fields, border))
+ return "".join(bits)
+
+ ##############################
+ # HTML PRINT/STRING METHODS #
+ ##############################
+
+ def print_html(self, start=0, end=None, fields=None, sortby=None, reversesort=False, format=True, header=True, border=True, hrules=FRAME, attributes=None):
+
+ """Print HTML formatted version of table in current state to stdout.
+
+ Arguments:
+
+ start - index of first data row to include in output
+ end - index of last data row to include in output PLUS ONE (list slice style)
+ fields - names of fields (columns) to include
+ sortby - name of field to sort rows by
+ format - should be True or False to attempt to format alignmet, padding, etc. or not
+ header - should be True or False to print a header showing field names or not
+ border - should be True or False to print or not print borders
+ hrules - include horizontal rule after each row
+ attributes - dictionary of name/value pairs to include as HTML attributes in the <table> tag"""
+
+ print self.get_html_string(start, end, fields, sortby, reversesort, format, header, border, hrules, attributes)
+
+ def get_html_string(self, start=0, end=None, fields=None, sortby=None, reversesort=False, format=True, header=True, border=True, hrules=FRAME, attributes=None):
+
+ """Return string representation of HTML formatted version of table in current state.
+
+ Arguments:
+
+ start - index of first data row to include in output
+ end - index of last data row to include in output PLUS ONE (list slice style)
+ fields - names of fields (columns) to include
+ sortby - name of
+ border - should be True or False to print or not print borders
+ format - should be True or False to attempt to format alignmet, padding, etc. or not
+ header - should be True or False to print a header showing field names or not
+ border - should be True or False to print or not print borders
+ hrules - include horizontal rule after each row
+ attributes - dictionary of name/value pairs to include as HTML attributes in the <table> tag"""
+
+ if self.caching:
+ key = cPickle.dumps((start, end, fields, format, header, border, hrules, sortby, reversesort, attributes))
+ if key in self.html_cache:
+ return self.html_cache[key]
+
+ if format:
+ tmp_html_func=self._get_formatted_html_string
+ else:
+ tmp_html_func=self._get_simple_html_string
+ string = tmp_html_func(start, end, fields, sortby, reversesort, header, border, hrules, attributes)
+
+ if self.caching:
+ self.html_cache[key] = string
+
+ return string
+
+ def _get_simple_html_string(self, start, end, fields, sortby, reversesort, header, border, hrules, attributes):
+
+ bits = []
+ # Slow but works
+ table_tag = '<table'
+ if border:
+ table_tag += ' border="1"'
+ if attributes:
+ for attr_name in attributes:
+ table_tag += ' %s="%s"' % (attr_name, attributes[attr_name])
+ table_tag += '>'
+ bits.append(table_tag)
+ # Headers
+ bits.append(" <tr>")
+ for field in self.fields:
+ if fields and field not in fields:
+ continue
+ bits.append(" <th>%s</th>" % cgi.escape(unicode(field)))
+ bits.append(" </tr>")
+ # Data
+ if sortby:
+ rows = self._get_sorted_rows(stard, end, sortby, reversesort)
+ else:
+ rows = self.rows
+ for row in self.rows:
+ bits.append(" <tr>")
+ for field, datum in zip(self.fields, row):
+ if fields and field not in fields:
+ continue
+ bits.append(" <td>%s</td>" % cgi.escape(unicode(datum)))
+ bits.append(" </tr>")
+ bits.append("</table>")
+ string = "\n".join(bits)
+
+ return string
+
+ def _get_formatted_html_string(self, start, end, fields, sortby, reversesort, header, border, hrules, attributes):
+
+ bits = []
+ # Slow but works
+ table_tag = '<table'
+ if border:
+ table_tag += ' border="1"'
+ if hrules == NONE:
+ table_tag += ' frame="vsides" rules="cols"'
+ if attributes:
+ for attr_name in attributes:
+ table_tag += ' %s="%s"' % (attr_name, attributes[attr_name])
+ table_tag += '>'
+ bits.append(table_tag)
+ # Headers
+ lpad, rpad = self._get_paddings()
+ if header:
+ bits.append(" <tr>")
+ for field in self.fields:
+ if fields and field not in fields:
+ continue
+ bits.append(" <th style=\"padding-left: %dem; padding-right: %dem; text-align: center\">%s</th>" % (lpad, rpad, cgi.escape(unicode(field))))
+ bits.append(" </tr>")
+ # Data
+ if sortby:
+ rows = self._get_sorted_rows(start, end, sortby, reversesort)
+ else:
+ rows = self.rows
+ for row in self.rows:
+ bits.append(" <tr>")
+ for field, align, datum in zip(self.fields, self.aligns, row):
+ if fields and field not in fields:
+ continue
+ if align == "l":
+ bits.append(" <td style=\"padding-left: %dem; padding-right: %dem; text-align: left\">%s</td>" % (lpad, rpad, cgi.escape(unicode(datum))))
+ elif align == "r":
+ bits.append(" <td style=\"padding-left: %dem; padding-right: %dem; text-align: right\">%s</td>" % (lpad, rpad, cgi.escape(unicode(datum))))
+ else:
+ bits.append(" <td style=\"padding-left: %dem; padding-right: %dem; text-align: center\">%s</td>" % (lpad, rpad, cgi.escape(unicode(datum))))
+ bits.append(" </tr>")
+ bits.append("</table>")
+ string = "\n".join(bits)
+
+ return string
+
+def main():
+
+ x = PrettyTable(["City name", "Area", "Population", "Annual Rainfall"])
+ x.set_field_align("City name", "l") # Left align city names
+ x.add_row(["Adelaide",1295, 1158259, 600.5])
+ x.add_row(["Brisbane",5905, 1857594, 1146.4])
+ x.add_row(["Darwin", 112, 120900, 1714.7])
+ x.add_row(["Hobart", 1357, 205556, 619.5])
+ x.add_row(["Sydney", 2058, 4336374, 1214.8])
+ x.add_row(["Melbourne", 1566, 3806092, 646.9])
+ x.add_row(["Perth", 5386, 1554769, 869.4])
+ print x
+
+ if len(sys.argv) > 1 and sys.argv[1] == "test":
+
+ # This "test suite" is hideous and provides poor, arbitrary coverage.
+ # I'll replace it with some proper unit tests Sometime Soon (TM).
+ # Promise.
+ print "Testing field subset selection:"
+ x.printt(fields=["City name","Population"])
+ print "Testing row subset selection:"
+ x.printt(start=2, end=5)
+ print "Testing hrules settings:"
+ print "FRAME:"
+ x.printt(hrules=FRAME)
+ print "ALL:"
+ x.printt(hrules=ALL)
+ print "NONE:"
+ x.printt(hrules=NONE)
+ print "Testing lack of headers:"
+ x.printt(header=False)
+ x.printt(header=False, border=False)
+ print "Testing lack of borders:"
+ x.printt(border=False)
+ print "Testing sorting:"
+ x.printt(sortby="City name")
+ x.printt(sortby="Annual Rainfall")
+ x.printt(sortby="Annual Rainfall", reversesort=True)
+ print "Testing padding parameter:"
+ x.set_padding_width(0)
+ x.printt()
+ x.set_padding_width(5)
+ x.printt()
+ x.set_left_padding(5)
+ x.set_right_padding(0)
+ x.printt()
+ x.set_right_padding(20)
+ x.printt()
+ x.set_left_padding(None)
+ x.set_right_padding(None)
+ x.set_padding_width(2)
+ print "Testing changing characters"
+ x.set_border_chars("*","*","*")
+ x.printt()
+ x.set_border_chars("!","~","o")
+ x.printt()
+ x.set_border_chars("|","-","+")
+ print "Testing everything at once:"
+ x.printt(start=2, end=5, fields=["City name","Population"], border=False, hrules=True)
+ print "Rebuilding by columns:"
+ x = PrettyTable()
+ x.add_column("City name", ["Adelaide", "Brisbane", "Darwin", "Hobart", "Sydney", "Melbourne", "Perth"])
+ x.add_column("Area", [1295, 5905, 112, 1357, 2058, 1566, 5385])
+ x.add_column("Population", [1158259, 1857594, 120900, 205556, 4336374, 3806092, 1554769])
+ x.add_column("Annual Rainfall", [600.5, 1146.4, 1714.7, 619.5, 1214.8, 646.9, 869.4])
+ x.printt()
+ print "Testing HTML:"
+ x.print_html()
+ x.print_html(border=False)
+ x.print_html(border=True)
+ x.print_html(format=False)
+ x.print_html(attributes={"name": "table", "id": "table"})
+
+if __name__ == "__main__":
+ main()
+
View
13 src/setup.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+from setuptools import setup
+
+setup(
+ name='prettytable',
+ version='TRUNK',
+ description='A simple Python library for easily displaying tabular data in a visually appealing ASCII table format',
+ author='Luke Maurits',
+ author_email='luke@maurits.id.au',
+ url='http://www.luke.maurits.id.au/software/prettytable',
+ license='http://www.luke.maurits.id.au/software/bsdlicense.txt',
+ py_modules=['prettytable']
+)

0 comments on commit 976de4d

Please sign in to comment.
Something went wrong with that request. Please try again.