You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafodion.apache.org by li...@apache.org on 2017/01/16 04:56:40 UTC
[3/5] incubator-trafodion git commit: [TRAFODION-2431] python
installer - add python lib dependencies
[TRAFODION-2431] python installer - add python lib dependencies
Project: http://git-wip-us.apache.org/repos/asf/incubator-trafodion/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-trafodion/commit/702b6ddd
Tree: http://git-wip-us.apache.org/repos/asf/incubator-trafodion/tree/702b6ddd
Diff: http://git-wip-us.apache.org/repos/asf/incubator-trafodion/diff/702b6ddd
Branch: refs/heads/master
Commit: 702b6ddd25c11efff85f46e42ffbd6322119f514
Parents: 73446d8
Author: Eason <hf...@gmail.com>
Authored: Tue Jan 10 11:37:23 2017 +0800
Committer: Eason <hf...@gmail.com>
Committed: Wed Jan 11 21:20:23 2017 +0800
----------------------------------------------------------------------
install/Makefile | 5 +-
install/python-installer/prettytable.py | 1475 +++++++++++++++
.../scripts/httplib2/__init__.py | 1714 ++++++++++++++++++
.../scripts/httplib2/iri2uri.py | 132 ++
.../python-installer/scripts/httplib2/socks.py | 438 +++++
licenses/Makefile | 7 +-
licenses/lic-pyinstall-src | 63 +
7 files changed, 3831 insertions(+), 3 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-trafodion/blob/702b6ddd/install/Makefile
----------------------------------------------------------------------
diff --git a/install/Makefile b/install/Makefile
index c6f5da6..5dc7048 100644
--- a/install/Makefile
+++ b/install/Makefile
@@ -47,7 +47,10 @@ installer/NOTICE: ../NOTICE
installer/DISCLAIMER: ../DISCLAIMER
cp -f $? $@
-python-installer/LICENSE: ../licenses/LICENSE-install
+../licenses/LICENSE-pyinstall:
+ cd $(@D) && $(MAKE) $(@F)
+
+python-installer/LICENSE: ../licenses/LICENSE-pyinstall
cp -f $? $@
python-installer/NOTICE: ../NOTICE
http://git-wip-us.apache.org/repos/asf/incubator-trafodion/blob/702b6ddd/install/python-installer/prettytable.py
----------------------------------------------------------------------
diff --git a/install/python-installer/prettytable.py b/install/python-installer/prettytable.py
new file mode 100644
index 0000000..8abb952
--- /dev/null
+++ b/install/python-installer/prettytable.py
@@ -0,0 +1,1475 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2009-2013, Luke Maurits <lu...@maurits.id.au>
+# All rights reserved.
+# With contributions from:
+# * Chris Clark
+# * Klein Stephane
+#
+# 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.
+
+__version__ = "0.7.2"
+
+import copy
+import csv
+import random
+import re
+import sys
+import textwrap
+import itertools
+import unicodedata
+
+py3k = sys.version_info[0] >= 3
+if py3k:
+ unicode = str
+ basestring = str
+ itermap = map
+ iterzip = zip
+ uni_chr = chr
+ from html.parser import HTMLParser
+else:
+ itermap = itertools.imap
+ iterzip = itertools.izip
+ uni_chr = unichr
+ from HTMLParser import HTMLParser
+
+if py3k and sys.version_info[1] >= 2:
+ from html import escape
+else:
+ from cgi import escape
+
+# hrule styles
+FRAME = 0
+ALL = 1
+NONE = 2
+HEADER = 3
+
+# Table styles
+DEFAULT = 10
+MSWORD_FRIENDLY = 11
+PLAIN_COLUMNS = 12
+RANDOM = 20
+
+_re = re.compile("\033\[[0-9;]*m")
+
+def _get_size(text):
+ lines = text.split("\n")
+ height = len(lines)
+ width = max([_str_block_width(line) for line in lines])
+ return (width, height)
+
+class PrettyTable(object):
+
+ def __init__(self, field_names=None, **kwargs):
+
+ """Return a new PrettyTable instance
+
+ Arguments:
+
+ encoding - Unicode encoding scheme used to decode any encoded input
+ field_names - list or tuple of field names
+ fields - list or tuple of field names to include in displays
+ 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)
+ header - print a header showing field names (True or False)
+ header_style - stylisation to apply to field names in header ("cap", "title", "upper", "lower" or None)
+ border - print a border around the table (True or False)
+ hrules - controls printing of horizontal rules after rows. Allowed values: FRAME, HEADER, ALL, NONE
+ vrules - controls printing of vertical rules between columns. Allowed values: FRAME, ALL, NONE
+ int_format - controls formatting of integer data
+ float_format - controls formatting of floating point data
+ padding_width - number of spaces on either side of column data (only used if left and right paddings are None)
+ left_padding_width - number of spaces on left hand side of column data
+ right_padding_width - number of spaces on right hand side of column data
+ vertical_char - single character string used to draw vertical lines
+ horizontal_char - single character string used to draw horizontal lines
+ junction_char - single character string used to draw line junctions
+ sortby - name of field to sort rows by
+ sort_key - sorting key function, applied to data points before sorting
+ valign - default valign for each row (None, "t", "m" or "b")
+ reversesort - True or False to sort in descending or ascending order"""
+
+ self.encoding = kwargs.get("encoding", "UTF-8")
+
+ # Data
+ self._field_names = []
+ self._align = {}
+ self._valign = {}
+ self._max_width = {}
+ self._rows = []
+ if field_names:
+ self.field_names = field_names
+ else:
+ self._widths = []
+
+ # Options
+ self._options = "start end fields header border sortby reversesort sort_key attributes format hrules vrules".split()
+ self._options.extend("int_format float_format padding_width left_padding_width right_padding_width".split())
+ self._options.extend("vertical_char horizontal_char junction_char header_style valign xhtml print_empty".split())
+ for option in self._options:
+ if option in kwargs:
+ self._validate_option(option, kwargs[option])
+ else:
+ kwargs[option] = None
+
+ self._start = kwargs["start"] or 0
+ self._end = kwargs["end"] or None
+ self._fields = kwargs["fields"] or None
+
+ if kwargs["header"] in (True, False):
+ self._header = kwargs["header"]
+ else:
+ self._header = True
+ self._header_style = kwargs["header_style"] or None
+ if kwargs["border"] in (True, False):
+ self._border = kwargs["border"]
+ else:
+ self._border = True
+ self._hrules = kwargs["hrules"] or FRAME
+ self._vrules = kwargs["vrules"] or ALL
+
+ self._sortby = kwargs["sortby"] or None
+ if kwargs["reversesort"] in (True, False):
+ self._reversesort = kwargs["reversesort"]
+ else:
+ self._reversesort = False
+ self._sort_key = kwargs["sort_key"] or (lambda x: x)
+
+ self._int_format = kwargs["int_format"] or {}
+ self._float_format = kwargs["float_format"] or {}
+ self._padding_width = kwargs["padding_width"] or 1
+ self._left_padding_width = kwargs["left_padding_width"] or None
+ self._right_padding_width = kwargs["right_padding_width"] or None
+
+ self._vertical_char = kwargs["vertical_char"] or self._unicode("|")
+ self._horizontal_char = kwargs["horizontal_char"] or self._unicode("-")
+ self._junction_char = kwargs["junction_char"] or self._unicode("+")
+
+ if kwargs["print_empty"] in (True, False):
+ self._print_empty = kwargs["print_empty"]
+ else:
+ self._print_empty = True
+ self._format = kwargs["format"] or False
+ self._xhtml = kwargs["xhtml"] or False
+ self._attributes = kwargs["attributes"] or {}
+
+ def _unicode(self, value):
+ if not isinstance(value, basestring):
+ value = str(value)
+ if not isinstance(value, unicode):
+ value = unicode(value, self.encoding, "strict")
+ return value
+
+ def _justify(self, text, width, align):
+ excess = width - _str_block_width(text)
+ if align == "l":
+ return text + excess * " "
+ elif align == "r":
+ return excess * " " + text
+ else:
+ if excess % 2:
+ # Uneven padding
+ # Put more space on right if text is of odd length...
+ if _str_block_width(text) % 2:
+ return (excess//2)*" " + text + (excess//2 + 1)*" "
+ # and more space on left if text is of even length
+ else:
+ return (excess//2 + 1)*" " + text + (excess//2)*" "
+ # Why distribute extra space this way? To match the behaviour of
+ # the inbuilt str.center() method.
+ else:
+ # Equal padding on either side
+ return (excess//2)*" " + text + (excess//2)*" "
+
+ def __getattr__(self, name):
+
+ if name == "rowcount":
+ return len(self._rows)
+ elif name == "colcount":
+ if self._field_names:
+ return len(self._field_names)
+ elif self._rows:
+ return len(self._rows[0])
+ else:
+ return 0
+ else:
+ raise AttributeError(name)
+
+ def __getitem__(self, index):
+
+ new = PrettyTable()
+ new.field_names = self.field_names
+ for attr in self._options:
+ setattr(new, "_"+attr, getattr(self, "_"+attr))
+ setattr(new, "_align", getattr(self, "_align"))
+ if isinstance(index, slice):
+ for row in self._rows[index]:
+ new.add_row(row)
+ elif isinstance(index, int):
+ new.add_row(self._rows[index])
+ else:
+ raise Exception("Index %s is invalid, must be an integer or slice" % str(index))
+ return new
+
+ if py3k:
+ def __str__(self):
+ return self.__unicode__()
+ else:
+ def __str__(self):
+ return self.__unicode__().encode(self.encoding)
+
+ def __unicode__(self):
+ return self.get_string()
+
+ ##############################
+ # ATTRIBUTE VALIDATORS #
+ ##############################
+
+ # The method _validate_option is all that should be used elsewhere in the code base to validate options.
+ # It will call the appropriate validation method for that option. The individual validation methods should
+ # never need to be called directly (although nothing bad will happen if they *are*).
+ # Validation happens in TWO places.
+ # Firstly, in the property setters defined in the ATTRIBUTE MANAGMENT section.
+ # Secondly, in the _get_options method, where keyword arguments are mixed with persistent settings
+
+ def _validate_option(self, option, val):
+ if option in ("field_names"):
+ self._validate_field_names(val)
+ elif option in ("start", "end", "max_width", "padding_width", "left_padding_width", "right_padding_width", "format"):
+ self._validate_nonnegative_int(option, val)
+ elif option in ("sortby"):
+ self._validate_field_name(option, val)
+ elif option in ("sort_key"):
+ self._validate_function(option, val)
+ elif option in ("hrules"):
+ self._validate_hrules(option, val)
+ elif option in ("vrules"):
+ self._validate_vrules(option, val)
+ elif option in ("fields"):
+ self._validate_all_field_names(option, val)
+ elif option in ("header", "border", "reversesort", "xhtml", "print_empty"):
+ self._validate_true_or_false(option, val)
+ elif option in ("header_style"):
+ self._validate_header_style(val)
+ elif option in ("int_format"):
+ self._validate_int_format(option, val)
+ elif option in ("float_format"):
+ self._validate_float_format(option, val)
+ elif option in ("vertical_char", "horizontal_char", "junction_char"):
+ self._validate_single_char(option, val)
+ elif option in ("attributes"):
+ self._validate_attributes(option, val)
+ else:
+ raise Exception("Unrecognised option: %s!" % option)
+
+ def _validate_field_names(self, val):
+ # Check for appropriate length
+ if self._field_names:
+ try:
+ assert len(val) == len(self._field_names)
+ except AssertionError:
+ raise Exception("Field name list has incorrect number of values, (actual) %d!=%d (expected)" % (len(val), len(self._field_names)))
+ if self._rows:
+ try:
+ assert len(val) == len(self._rows[0])
+ except AssertionError:
+ raise Exception("Field name list has incorrect number of values, (actual) %d!=%d (expected)" % (len(val), len(self._rows[0])))
+ # Check for uniqueness
+ try:
+ assert len(val) == len(set(val))
+ except AssertionError:
+ raise Exception("Field names must be unique!")
+
+ def _validate_header_style(self, val):
+ try:
+ assert val in ("cap", "title", "upper", "lower", None)
+ except AssertionError:
+ raise Exception("Invalid header style, use cap, title, upper, lower or None!")
+
+ def _validate_align(self, val):
+ try:
+ assert val in ["l","c","r"]
+ except AssertionError:
+ raise Exception("Alignment %s is invalid, use l, c or r!" % val)
+
+ def _validate_valign(self, val):
+ try:
+ assert val in ["t","m","b",None]
+ except AssertionError:
+ raise Exception("Alignment %s is invalid, use t, m, b or None!" % val)
+
+ def _validate_nonnegative_int(self, name, val):
+ try:
+ assert int(val) >= 0
+ except AssertionError:
+ raise Exception("Invalid value for %s: %s!" % (name, self._unicode(val)))
+
+ def _validate_true_or_false(self, name, val):
+ try:
+ assert val in (True, False)
+ except AssertionError:
+ raise Exception("Invalid value for %s! Must be True or False." % name)
+
+ def _validate_int_format(self, name, val):
+ if val == "":
+ return
+ try:
+ assert type(val) in (str, unicode)
+ assert val.isdigit()
+ except AssertionError:
+ raise Exception("Invalid value for %s! Must be an integer format string." % name)
+
+ def _validate_float_format(self, name, val):
+ if val == "":
+ return
+ try:
+ assert type(val) in (str, unicode)
+ assert "." in val
+ bits = val.split(".")
+ assert len(bits) <= 2
+ assert bits[0] == "" or bits[0].isdigit()
+ assert bits[1] == "" or bits[1].isdigit()
+ except AssertionError:
+ raise Exception("Invalid value for %s! Must be a float format string." % name)
+
+ def _validate_function(self, name, val):
+ try:
+ assert hasattr(val, "__call__")
+ except AssertionError:
+ raise Exception("Invalid value for %s! Must be a function." % name)
+
+ def _validate_hrules(self, name, val):
+ try:
+ assert val in (ALL, FRAME, HEADER, NONE)
+ except AssertionError:
+ raise Exception("Invalid value for %s! Must be ALL, FRAME, HEADER or NONE." % name)
+
+ def _validate_vrules(self, name, val):
+ try:
+ assert val in (ALL, FRAME, NONE)
+ except AssertionError:
+ raise Exception("Invalid value for %s! Must be ALL, FRAME, or NONE." % name)
+
+ def _validate_field_name(self, name, val):
+ try:
+ assert (val in self._field_names) or (val is None)
+ except AssertionError:
+ raise Exception("Invalid field name: %s!" % val)
+
+ def _validate_all_field_names(self, name, val):
+ try:
+ for x in val:
+ self._validate_field_name(name, x)
+ except AssertionError:
+ raise Exception("fields must be a sequence of field names!")
+
+ def _validate_single_char(self, name, val):
+ try:
+ assert _str_block_width(val) == 1
+ except AssertionError:
+ raise Exception("Invalid value for %s! Must be a string of length 1." % name)
+
+ def _validate_attributes(self, name, val):
+ try:
+ assert isinstance(val, dict)
+ except AssertionError:
+ raise Exception("attributes must be a dictionary of name/value pairs!")
+
+ ##############################
+ # ATTRIBUTE MANAGEMENT #
+ ##############################
+
+ def _get_field_names(self):
+ return self._field_names
+ """The names of the fields
+
+ Arguments:
+
+ fields - list or tuple of field names"""
+ def _set_field_names(self, val):
+ val = [self._unicode(x) for x in val]
+ self._validate_option("field_names", val)
+ if self._field_names:
+ old_names = self._field_names[:]
+ self._field_names = val
+ if self._align and old_names:
+ for old_name, new_name in zip(old_names, val):
+ self._align[new_name] = self._align[old_name]
+ for old_name in old_names:
+ if old_name not in self._align:
+ self._align.pop(old_name)
+ else:
+ for field in self._field_names:
+ self._align[field] = "c"
+ if self._valign and old_names:
+ for old_name, new_name in zip(old_names, val):
+ self._valign[new_name] = self._valign[old_name]
+ for old_name in old_names:
+ if old_name not in self._valign:
+ self._valign.pop(old_name)
+ else:
+ for field in self._field_names:
+ self._valign[field] = "t"
+ field_names = property(_get_field_names, _set_field_names)
+
+ def _get_align(self):
+ return self._align
+ def _set_align(self, val):
+ self._validate_align(val)
+ for field in self._field_names:
+ self._align[field] = val
+ align = property(_get_align, _set_align)
+
+ def _get_valign(self):
+ return self._valign
+ def _set_valign(self, val):
+ self._validate_valign(val)
+ for field in self._field_names:
+ self._valign[field] = val
+ valign = property(_get_valign, _set_valign)
+
+ def _get_max_width(self):
+ return self._max_width
+ def _set_max_width(self, val):
+ self._validate_option("max_width", val)
+ for field in self._field_names:
+ self._max_width[field] = val
+ max_width = property(_get_max_width, _set_max_width)
+
+ def _get_fields(self):
+ """List or tuple of field names to include in displays
+
+ Arguments:
+
+ fields - list or tuple of field names to include in displays"""
+ return self._fields
+ def _set_fields(self, val):
+ self._validate_option("fields", val)
+ self._fields = val
+ fields = property(_get_fields, _set_fields)
+
+ def _get_start(self):
+ """Start index of the range of rows to print
+
+ Arguments:
+
+ start - index of first data row to include in output"""
+ return self._start
+
+ def _set_start(self, val):
+ self._validate_option("start", val)
+ self._start = val
+ start = property(_get_start, _set_start)
+
+ def _get_end(self):
+ """End index of the range of rows to print
+
+ Arguments:
+
+ end - index of last data row to include in output PLUS ONE (list slice style)"""
+ return self._end
+ def _set_end(self, val):
+ self._validate_option("end", val)
+ self._end = val
+ end = property(_get_end, _set_end)
+
+ def _get_sortby(self):
+ """Name of field by which to sort rows
+
+ Arguments:
+
+ sortby - field name to sort by"""
+ return self._sortby
+ def _set_sortby(self, val):
+ self._validate_option("sortby", val)
+ self._sortby = val
+ sortby = property(_get_sortby, _set_sortby)
+
+ def _get_reversesort(self):
+ """Controls direction of sorting (ascending vs descending)
+
+ Arguments:
+
+ reveresort - set to True to sort by descending order, or False to sort by ascending order"""
+ return self._reversesort
+ def _set_reversesort(self, val):
+ self._validate_option("reversesort", val)
+ self._reversesort = val
+ reversesort = property(_get_reversesort, _set_reversesort)
+
+ def _get_sort_key(self):
+ """Sorting key function, applied to data points before sorting
+
+ Arguments:
+
+ sort_key - a function which takes one argument and returns something to be sorted"""
+ return self._sort_key
+ def _set_sort_key(self, val):
+ self._validate_option("sort_key", val)
+ self._sort_key = val
+ sort_key = property(_get_sort_key, _set_sort_key)
+
+ def _get_header(self):
+ """Controls printing of table header with field names
+
+ Arguments:
+
+ header - print a header showing field names (True or False)"""
+ return self._header
+ def _set_header(self, val):
+ self._validate_option("header", val)
+ self._header = val
+ header = property(_get_header, _set_header)
+
+ def _get_header_style(self):
+ """Controls stylisation applied to field names in header
+
+ Arguments:
+
+ header_style - stylisation to apply to field names in header ("cap", "title", "upper", "lower" or None)"""
+ return self._header_style
+ def _set_header_style(self, val):
+ self._validate_header_style(val)
+ self._header_style = val
+ header_style = property(_get_header_style, _set_header_style)
+
+ def _get_border(self):
+ """Controls printing of border around table
+
+ Arguments:
+
+ border - print a border around the table (True or False)"""
+ return self._border
+ def _set_border(self, val):
+ self._validate_option("border", val)
+ self._border = val
+ border = property(_get_border, _set_border)
+
+ def _get_hrules(self):
+ """Controls printing of horizontal rules after rows
+
+ Arguments:
+
+ hrules - horizontal rules style. Allowed values: FRAME, ALL, HEADER, NONE"""
+ return self._hrules
+ def _set_hrules(self, val):
+ self._validate_option("hrules", val)
+ self._hrules = val
+ hrules = property(_get_hrules, _set_hrules)
+
+ def _get_vrules(self):
+ """Controls printing of vertical rules between columns
+
+ Arguments:
+
+ vrules - vertical rules style. Allowed values: FRAME, ALL, NONE"""
+ return self._vrules
+ def _set_vrules(self, val):
+ self._validate_option("vrules", val)
+ self._vrules = val
+ vrules = property(_get_vrules, _set_vrules)
+
+ def _get_int_format(self):
+ """Controls formatting of integer data
+ Arguments:
+
+ int_format - integer format string"""
+ return self._int_format
+ def _set_int_format(self, val):
+# self._validate_option("int_format", val)
+ for field in self._field_names:
+ self._int_format[field] = val
+ int_format = property(_get_int_format, _set_int_format)
+
+ def _get_float_format(self):
+ """Controls formatting of floating point data
+ Arguments:
+
+ float_format - floating point format string"""
+ return self._float_format
+ def _set_float_format(self, val):
+# self._validate_option("float_format", val)
+ for field in self._field_names:
+ self._float_format[field] = val
+ float_format = property(_get_float_format, _set_float_format)
+
+ def _get_padding_width(self):
+ """The number of empty spaces between a column's edge and its content
+
+ Arguments:
+
+ padding_width - number of spaces, must be a positive integer"""
+ return self._padding_width
+ def _set_padding_width(self, val):
+ self._validate_option("padding_width", val)
+ self._padding_width = val
+ padding_width = property(_get_padding_width, _set_padding_width)
+
+ def _get_left_padding_width(self):
+ """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"""
+ return self._left_padding_width
+ def _set_left_padding_width(self, val):
+ self._validate_option("left_padding_width", val)
+ self._left_padding_width = val
+ left_padding_width = property(_get_left_padding_width, _set_left_padding_width)
+
+ def _get_right_padding_width(self):
+ """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"""
+ return self._right_padding_width
+ def _set_right_padding_width(self, val):
+ self._validate_option("right_padding_width", val)
+ self._right_padding_width = val
+ right_padding_width = property(_get_right_padding_width, _set_right_padding_width)
+
+ def _get_vertical_char(self):
+ """The charcter used when printing table borders to draw vertical lines
+
+ Arguments:
+
+ vertical_char - single character string used to draw vertical lines"""
+ return self._vertical_char
+ def _set_vertical_char(self, val):
+ val = self._unicode(val)
+ self._validate_option("vertical_char", val)
+ self._vertical_char = val
+ vertical_char = property(_get_vertical_char, _set_vertical_char)
+
+ def _get_horizontal_char(self):
+ """The charcter used when printing table borders to draw horizontal lines
+
+ Arguments:
+
+ horizontal_char - single character string used to draw horizontal lines"""
+ return self._horizontal_char
+ def _set_horizontal_char(self, val):
+ val = self._unicode(val)
+ self._validate_option("horizontal_char", val)
+ self._horizontal_char = val
+ horizontal_char = property(_get_horizontal_char, _set_horizontal_char)
+
+ def _get_junction_char(self):
+ """The charcter used when printing table borders to draw line junctions
+
+ Arguments:
+
+ junction_char - single character string used to draw line junctions"""
+ return self._junction_char
+ def _set_junction_char(self, val):
+ val = self._unicode(val)
+ self._validate_option("vertical_char", val)
+ self._junction_char = val
+ junction_char = property(_get_junction_char, _set_junction_char)
+
+ def _get_format(self):
+ """Controls whether or not HTML tables are formatted to match styling options
+
+ Arguments:
+
+ format - True or False"""
+ return self._format
+ def _set_format(self, val):
+ self._validate_option("format", val)
+ self._format = val
+ format = property(_get_format, _set_format)
+
+ def _get_print_empty(self):
+ """Controls whether or not empty tables produce a header and frame or just an empty string
+
+ Arguments:
+
+ print_empty - True or False"""
+ return self._print_empty
+ def _set_print_empty(self, val):
+ self._validate_option("print_empty", val)
+ self._print_empty = val
+ print_empty = property(_get_print_empty, _set_print_empty)
+
+ def _get_attributes(self):
+ """A dictionary of HTML attribute name/value pairs to be included in the <table> tag when printing HTML
+
+ Arguments:
+
+ attributes - dictionary of attributes"""
+ return self._attributes
+ def _set_attributes(self, val):
+ self._validate_option("attributes", val)
+ self._attributes = val
+ attributes = property(_get_attributes, _set_attributes)
+
+ ##############################
+ # OPTION MIXER #
+ ##############################
+
+ def _get_options(self, kwargs):
+
+ options = {}
+ for option in self._options:
+ if option in kwargs:
+ self._validate_option(option, kwargs[option])
+ options[option] = kwargs[option]
+ else:
+ options[option] = getattr(self, "_"+option)
+ return options
+
+ ##############################
+ # PRESET STYLE LOGIC #
+ ##############################
+
+ def set_style(self, style):
+
+ if style == DEFAULT:
+ self._set_default_style()
+ elif style == MSWORD_FRIENDLY:
+ self._set_msword_style()
+ elif style == PLAIN_COLUMNS:
+ self._set_columns_style()
+ elif style == RANDOM:
+ self._set_random_style()
+ else:
+ raise Exception("Invalid pre-set style!")
+
+ def _set_default_style(self):
+
+ self.header = True
+ self.border = True
+ self._hrules = FRAME
+ self._vrules = ALL
+ self.padding_width = 1
+ self.left_padding_width = 1
+ self.right_padding_width = 1
+ self.vertical_char = "|"
+ self.horizontal_char = "-"
+ self.junction_char = "+"
+
+ def _set_msword_style(self):
+
+ self.header = True
+ self.border = True
+ self._hrules = NONE
+ self.padding_width = 1
+ self.left_padding_width = 1
+ self.right_padding_width = 1
+ self.vertical_char = "|"
+
+ def _set_columns_style(self):
+
+ self.header = True
+ self.border = False
+ self.padding_width = 1
+ self.left_padding_width = 0
+ self.right_padding_width = 8
+
+ def _set_random_style(self):
+
+ # Just for fun!
+ self.header = random.choice((True, False))
+ self.border = random.choice((True, False))
+ self._hrules = random.choice((ALL, FRAME, HEADER, NONE))
+ self._vrules = random.choice((ALL, FRAME, NONE))
+ self.left_padding_width = random.randint(0,5)
+ self.right_padding_width = random.randint(0,5)
+ self.vertical_char = random.choice("~!@#$%^&*()_+|-=\{}[];':\",./;<>?")
+ self.horizontal_char = random.choice("~!@#$%^&*()_+|-=\{}[];':\",./;<>?")
+ self.junction_char = random.choice("~!@#$%^&*()_+|-=\{}[];':\",./;<>?")
+
+ ##############################
+ # 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 self._field_names and len(row) != len(self._field_names):
+ raise Exception("Row has incorrect number of values, (actual) %d!=%d (expected)" %(len(row),len(self._field_names)))
+ if not self._field_names:
+ self.field_names = [("Field %d" % (n+1)) for n in range(0,len(row))]
+ self._rows.append(list(row))
+
+ def del_row(self, row_index):
+
+ """Delete a row to the table
+
+ Arguments:
+
+ row_index - The index of the row you want to delete. Indexing starts at 0."""
+
+ if row_index > len(self._rows)-1:
+ raise Exception("Cant delete row at index %d, table only has %d rows!" % (row_index, len(self._rows)))
+ del self._rows[row_index]
+
+ def add_column(self, fieldname, column, align="c", valign="t"):
+
+ """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
+ valign - desired vertical alignment for new columns - "t" for top, "m" for middle and "b" for bottom"""
+
+ if len(self._rows) in (0, len(column)):
+ self._validate_align(align)
+ self._validate_valign(valign)
+ self._field_names.append(fieldname)
+ self._align[fieldname] = align
+ self._valign[fieldname] = valign
+ for i in range(0, len(column)):
+ if len(self._rows) < i+1:
+ self._rows.append([])
+ self._rows[i].append(column[i])
+ else:
+ raise Exception("Column length %d does not match number of rows %d!" % (len(column), len(self._rows)))
+
+ def clear_rows(self):
+
+ """Delete all rows from the table but keep the current field names"""
+
+ self._rows = []
+
+ def clear(self):
+
+ """Delete all rows and field names from the table, maintaining nothing but styling options"""
+
+ self._rows = []
+ self._field_names = []
+ self._widths = []
+
+ ##############################
+ # MISC PUBLIC METHODS #
+ ##############################
+
+ def copy(self):
+ return copy.deepcopy(self)
+
+ ##############################
+ # MISC PRIVATE METHODS #
+ ##############################
+
+ def _format_value(self, field, value):
+ if isinstance(value, int) and field in self._int_format:
+ value = self._unicode(("%%%sd" % self._int_format[field]) % value)
+ elif isinstance(value, float) and field in self._float_format:
+ value = self._unicode(("%%%sf" % self._float_format[field]) % value)
+ return self._unicode(value)
+
+ def _compute_widths(self, rows, options):
+ if options["header"]:
+ widths = [_get_size(field)[0] for field in self._field_names]
+ else:
+ widths = len(self.field_names) * [0]
+ for row in rows:
+ for index, value in enumerate(row):
+ fieldname = self.field_names[index]
+ if fieldname in self.max_width:
+ widths[index] = max(widths[index], min(_get_size(value)[0], self.max_width[fieldname]))
+ else:
+ widths[index] = max(widths[index], _get_size(value)[0])
+ self._widths = widths
+
+ def _get_padding_widths(self, options):
+
+ if options["left_padding_width"] is not None:
+ lpad = options["left_padding_width"]
+ else:
+ lpad = options["padding_width"]
+ if options["right_padding_width"] is not None:
+ rpad = options["right_padding_width"]
+ else:
+ rpad = options["padding_width"]
+ return lpad, rpad
+
+ def _get_rows(self, options):
+ """Return only those data rows that should be printed, based on slicing and sorting.
+
+ Arguments:
+
+ options - dictionary of option settings."""
+
+ # Make a copy of only those rows in the slice range
+ rows = copy.deepcopy(self._rows[options["start"]:options["end"]])
+ # Sort if necessary
+ if options["sortby"]:
+ sortindex = self._field_names.index(options["sortby"])
+ # Decorate
+ rows = [[row[sortindex]]+row for row in rows]
+ # Sort
+ rows.sort(reverse=options["reversesort"], key=options["sort_key"])
+ # Undecorate
+ rows = [row[1:] for row in rows]
+ return rows
+
+ def _format_row(self, row, options):
+ return [self._format_value(field, value) for (field, value) in zip(self._field_names, row)]
+
+ def _format_rows(self, rows, options):
+ return [self._format_row(row, options) for row in rows]
+
+ ##############################
+ # PLAIN TEXT STRING METHODS #
+ ##############################
+
+ def get_string(self, **kwargs):
+
+ """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
+ header - print a header showing field names (True or False)
+ border - print a border around the table (True or False)
+ hrules - controls printing of horizontal rules after rows. Allowed values: ALL, FRAME, HEADER, NONE
+ vrules - controls printing of vertical rules between columns. Allowed values: FRAME, ALL, NONE
+ int_format - controls formatting of integer data
+ float_format - controls formatting of floating point data
+ padding_width - number of spaces on either side of column data (only used if left and right paddings are None)
+ left_padding_width - number of spaces on left hand side of column data
+ right_padding_width - number of spaces on right hand side of column data
+ vertical_char - single character string used to draw vertical lines
+ horizontal_char - single character string used to draw horizontal lines
+ junction_char - single character string used to draw line junctions
+ sortby - name of field to sort rows by
+ sort_key - sorting key function, applied to data points before sorting
+ reversesort - True or False to sort in descending or ascending order
+ print empty - if True, stringify just the header for an empty table, if False return an empty string """
+
+ options = self._get_options(kwargs)
+
+ lines = []
+
+ # Don't think too hard about an empty table
+ # Is this the desired behaviour? Maybe we should still print the header?
+ if self.rowcount == 0 and (not options["print_empty"] or not options["border"]):
+ return ""
+
+ # Get the rows we need to print, taking into account slicing, sorting, etc.
+ rows = self._get_rows(options)
+
+ # Turn all data in all rows into Unicode, formatted as desired
+ formatted_rows = self._format_rows(rows, options)
+
+ # Compute column widths
+ self._compute_widths(formatted_rows, options)
+
+ # Add header or top of border
+ self._hrule = self._stringify_hrule(options)
+ if options["header"]:
+ lines.append(self._stringify_header(options))
+ elif options["border"] and options["hrules"] in (ALL, FRAME):
+ lines.append(self._hrule)
+
+ # Add rows
+ for row in formatted_rows:
+ lines.append(self._stringify_row(row, options))
+
+ # Add bottom of border
+ if options["border"] and options["hrules"] == FRAME:
+ lines.append(self._hrule)
+
+ return self._unicode("\n").join(lines)
+
+ def _stringify_hrule(self, options):
+
+ if not options["border"]:
+ return ""
+ lpad, rpad = self._get_padding_widths(options)
+ if options['vrules'] in (ALL, FRAME):
+ bits = [options["junction_char"]]
+ else:
+ bits = [options["horizontal_char"]]
+ # For tables with no data or fieldnames
+ if not self._field_names:
+ bits.append(options["junction_char"])
+ return "".join(bits)
+ for field, width in zip(self._field_names, self._widths):
+ if options["fields"] and field not in options["fields"]:
+ continue
+ bits.append((width+lpad+rpad)*options["horizontal_char"])
+ if options['vrules'] == ALL:
+ bits.append(options["junction_char"])
+ else:
+ bits.append(options["horizontal_char"])
+ if options["vrules"] == FRAME:
+ bits.pop()
+ bits.append(options["junction_char"])
+ return "".join(bits)
+
+ def _stringify_header(self, options):
+
+ bits = []
+ lpad, rpad = self._get_padding_widths(options)
+ if options["border"]:
+ if options["hrules"] in (ALL, FRAME):
+ bits.append(self._hrule)
+ bits.append("\n")
+ if options["vrules"] in (ALL, FRAME):
+ bits.append(options["vertical_char"])
+ else:
+ bits.append(" ")
+ # For tables with no data or field names
+ if not self._field_names:
+ if options["vrules"] in (ALL, FRAME):
+ bits.append(options["vertical_char"])
+ else:
+ bits.append(" ")
+ for field, width, in zip(self._field_names, self._widths):
+ if options["fields"] and field not in options["fields"]:
+ continue
+ if self._header_style == "cap":
+ fieldname = field.capitalize()
+ elif self._header_style == "title":
+ fieldname = field.title()
+ elif self._header_style == "upper":
+ fieldname = field.upper()
+ elif self._header_style == "lower":
+ fieldname = field.lower()
+ else:
+ fieldname = field
+ bits.append(" " * lpad + self._justify(fieldname, width, self._align[field]) + " " * rpad)
+ if options["border"]:
+ if options["vrules"] == ALL:
+ bits.append(options["vertical_char"])
+ else:
+ bits.append(" ")
+ # If vrules is FRAME, then we just appended a space at the end
+ # of the last field, when we really want a vertical character
+ if options["border"] and options["vrules"] == FRAME:
+ bits.pop()
+ bits.append(options["vertical_char"])
+ if options["border"] and options["hrules"] != NONE:
+ bits.append("\n")
+ bits.append(self._hrule)
+ return "".join(bits)
+
+ def _stringify_row(self, row, options):
+
+ for index, field, value, width, in zip(range(0,len(row)), self._field_names, row, self._widths):
+ # Enforce max widths
+ lines = value.split("\n")
+ new_lines = []
+ for line in lines:
+ if _str_block_width(line) > width:
+ line = textwrap.fill(line, width)
+ new_lines.append(line)
+ lines = new_lines
+ value = "\n".join(lines)
+ row[index] = value
+
+ row_height = 0
+ for c in row:
+ h = _get_size(c)[1]
+ if h > row_height:
+ row_height = h
+
+ bits = []
+ lpad, rpad = self._get_padding_widths(options)
+ for y in range(0, row_height):
+ bits.append([])
+ if options["border"]:
+ if options["vrules"] in (ALL, FRAME):
+ bits[y].append(self.vertical_char)
+ else:
+ bits[y].append(" ")
+
+ for field, value, width, in zip(self._field_names, row, self._widths):
+
+ valign = self._valign[field]
+ lines = value.split("\n")
+ dHeight = row_height - len(lines)
+ if dHeight:
+ if valign == "m":
+ lines = [""] * int(dHeight / 2) + lines + [""] * (dHeight - int(dHeight / 2))
+ elif valign == "b":
+ lines = [""] * dHeight + lines
+ else:
+ lines = lines + [""] * dHeight
+
+ y = 0
+ for l in lines:
+ if options["fields"] and field not in options["fields"]:
+ continue
+
+ bits[y].append(" " * lpad + self._justify(l, width, self._align[field]) + " " * rpad)
+ if options["border"]:
+ if options["vrules"] == ALL:
+ bits[y].append(self.vertical_char)
+ else:
+ bits[y].append(" ")
+ y += 1
+
+ # If vrules is FRAME, then we just appended a space at the end
+ # of the last field, when we really want a vertical character
+ for y in range(0, row_height):
+ if options["border"] and options["vrules"] == FRAME:
+ bits[y].pop()
+ bits[y].append(options["vertical_char"])
+
+ if options["border"] and options["hrules"]== ALL:
+ bits[row_height-1].append("\n")
+ bits[row_height-1].append(self._hrule)
+
+ for y in range(0, row_height):
+ bits[y] = "".join(bits[y])
+
+ return "\n".join(bits)
+
+ ##############################
+ # HTML STRING METHODS #
+ ##############################
+
+ def get_html_string(self, **kwargs):
+
+ """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
+ header - print a header showing field names (True or False)
+ border - print a border around the table (True or False)
+ hrules - controls printing of horizontal rules after rows. Allowed values: ALL, FRAME, HEADER, NONE
+ vrules - controls printing of vertical rules between columns. Allowed values: FRAME, ALL, NONE
+ int_format - controls formatting of integer data
+ float_format - controls formatting of floating point data
+ padding_width - number of spaces on either side of column data (only used if left and right paddings are None)
+ left_padding_width - number of spaces on left hand side of column data
+ right_padding_width - number of spaces on right hand side of column data
+ sortby - name of field to sort rows by
+ sort_key - sorting key function, applied to data points before sorting
+ attributes - dictionary of name/value pairs to include as HTML attributes in the <table> tag
+ xhtml - print <br/> tags if True, <br> tags if false"""
+
+ options = self._get_options(kwargs)
+
+ if options["format"]:
+ string = self._get_formatted_html_string(options)
+ else:
+ string = self._get_simple_html_string(options)
+
+ return string
+
+ def _get_simple_html_string(self, options):
+
+ lines = []
+ if options["xhtml"]:
+ linebreak = "<br/>"
+ else:
+ linebreak = "<br>"
+
+ open_tag = []
+ open_tag.append("<table")
+ if options["attributes"]:
+ for attr_name in options["attributes"]:
+ open_tag.append(" %s=\"%s\"" % (attr_name, options["attributes"][attr_name]))
+ open_tag.append(">")
+ lines.append("".join(open_tag))
+
+ # Headers
+ if options["header"]:
+ lines.append(" <tr>")
+ for field in self._field_names:
+ if options["fields"] and field not in options["fields"]:
+ continue
+ lines.append(" <th>%s</th>" % escape(field).replace("\n", linebreak))
+ lines.append(" </tr>")
+
+ # Data
+ rows = self._get_rows(options)
+ formatted_rows = self._format_rows(rows, options)
+ for row in formatted_rows:
+ lines.append(" <tr>")
+ for field, datum in zip(self._field_names, row):
+ if options["fields"] and field not in options["fields"]:
+ continue
+ lines.append(" <td>%s</td>" % escape(datum).replace("\n", linebreak))
+ lines.append(" </tr>")
+
+ lines.append("</table>")
+
+ return self._unicode("\n").join(lines)
+
+ def _get_formatted_html_string(self, options):
+
+ lines = []
+ lpad, rpad = self._get_padding_widths(options)
+ if options["xhtml"]:
+ linebreak = "<br/>"
+ else:
+ linebreak = "<br>"
+
+ open_tag = []
+ open_tag.append("<table")
+ if options["border"]:
+ if options["hrules"] == ALL and options["vrules"] == ALL:
+ open_tag.append(" frame=\"box\" rules=\"all\"")
+ elif options["hrules"] == FRAME and options["vrules"] == FRAME:
+ open_tag.append(" frame=\"box\"")
+ elif options["hrules"] == FRAME and options["vrules"] == ALL:
+ open_tag.append(" frame=\"box\" rules=\"cols\"")
+ elif options["hrules"] == FRAME:
+ open_tag.append(" frame=\"hsides\"")
+ elif options["hrules"] == ALL:
+ open_tag.append(" frame=\"hsides\" rules=\"rows\"")
+ elif options["vrules"] == FRAME:
+ open_tag.append(" frame=\"vsides\"")
+ elif options["vrules"] == ALL:
+ open_tag.append(" frame=\"vsides\" rules=\"cols\"")
+ if options["attributes"]:
+ for attr_name in options["attributes"]:
+ open_tag.append(" %s=\"%s\"" % (attr_name, options["attributes"][attr_name]))
+ open_tag.append(">")
+ lines.append("".join(open_tag))
+
+ # Headers
+ if options["header"]:
+ lines.append(" <tr>")
+ for field in self._field_names:
+ if options["fields"] and field not in options["fields"]:
+ continue
+ lines.append(" <th style=\"padding-left: %dem; padding-right: %dem; text-align: center\">%s</th>" % (lpad, rpad, escape(field).replace("\n", linebreak)))
+ lines.append(" </tr>")
+
+ # Data
+ rows = self._get_rows(options)
+ formatted_rows = self._format_rows(rows, options)
+ aligns = []
+ valigns = []
+ for field in self._field_names:
+ aligns.append({ "l" : "left", "r" : "right", "c" : "center" }[self._align[field]])
+ valigns.append({"t" : "top", "m" : "middle", "b" : "bottom"}[self._valign[field]])
+ for row in formatted_rows:
+ lines.append(" <tr>")
+ for field, datum, align, valign in zip(self._field_names, row, aligns, valigns):
+ if options["fields"] and field not in options["fields"]:
+ continue
+ lines.append(" <td style=\"padding-left: %dem; padding-right: %dem; text-align: %s; vertical-align: %s\">%s</td>" % (lpad, rpad, align, valign, escape(datum).replace("\n", linebreak)))
+ lines.append(" </tr>")
+ lines.append("</table>")
+
+ return self._unicode("\n").join(lines)
+
+##############################
+# UNICODE WIDTH FUNCTIONS #
+##############################
+
+def _char_block_width(char):
+ # Basic Latin, which is probably the most common case
+ #if char in xrange(0x0021, 0x007e):
+ #if char >= 0x0021 and char <= 0x007e:
+ if 0x0021 <= char <= 0x007e:
+ return 1
+ # Chinese, Japanese, Korean (common)
+ if 0x4e00 <= char <= 0x9fff:
+ return 2
+ # Hangul
+ if 0xac00 <= char <= 0xd7af:
+ return 2
+ # Combining?
+ if unicodedata.combining(uni_chr(char)):
+ return 0
+ # Hiragana and Katakana
+ if 0x3040 <= char <= 0x309f or 0x30a0 <= char <= 0x30ff:
+ return 2
+ # Full-width Latin characters
+ if 0xff01 <= char <= 0xff60:
+ return 2
+ # CJK punctuation
+ if 0x3000 <= char <= 0x303e:
+ return 2
+ # Backspace and delete
+ if char in (0x0008, 0x007f):
+ return -1
+ # Other control characters
+ elif char in (0x0000, 0x001f):
+ return 0
+ # Take a guess
+ return 1
+
+def _str_block_width(val):
+
+ return sum(itermap(_char_block_width, itermap(ord, _re.sub("", val))))
+
+##############################
+# TABLE FACTORIES #
+##############################
+
+def from_csv(fp, field_names = None, **kwargs):
+
+ dialect = csv.Sniffer().sniff(fp.read(1024))
+ fp.seek(0)
+ reader = csv.reader(fp, dialect)
+
+ table = PrettyTable(**kwargs)
+ if field_names:
+ table.field_names = field_names
+ else:
+ if py3k:
+ table.field_names = [x.strip() for x in next(reader)]
+ else:
+ table.field_names = [x.strip() for x in reader.next()]
+
+ for row in reader:
+ table.add_row([x.strip() for x in row])
+
+ return table
+
+def from_db_cursor(cursor, **kwargs):
+
+ if cursor.description:
+ table = PrettyTable(**kwargs)
+ table.field_names = [col[0] for col in cursor.description]
+ for row in cursor.fetchall():
+ table.add_row(row)
+ return table
+
+class TableHandler(HTMLParser):
+
+ def __init__(self, **kwargs):
+ HTMLParser.__init__(self)
+ self.kwargs = kwargs
+ self.tables = []
+ self.last_row = []
+ self.rows = []
+ self.max_row_width = 0
+ self.active = None
+ self.last_content = ""
+ self.is_last_row_header = False
+
+ def handle_starttag(self,tag, attrs):
+ self.active = tag
+ if tag == "th":
+ self.is_last_row_header = True
+
+ def handle_endtag(self,tag):
+ if tag in ["th", "td"]:
+ stripped_content = self.last_content.strip()
+ self.last_row.append(stripped_content)
+ if tag == "tr":
+ self.rows.append(
+ (self.last_row, self.is_last_row_header))
+ self.max_row_width = max(self.max_row_width, len(self.last_row))
+ self.last_row = []
+ self.is_last_row_header = False
+ if tag == "table":
+ table = self.generate_table(self.rows)
+ self.tables.append(table)
+ self.rows = []
+ self.last_content = " "
+ self.active = None
+
+
+ def handle_data(self, data):
+ self.last_content += data
+
+ def generate_table(self, rows):
+ """
+ Generates from a list of rows a PrettyTable object.
+ """
+ table = PrettyTable(**self.kwargs)
+ for row in self.rows:
+ if len(row[0]) < self.max_row_width:
+ appends = self.max_row_width - len(row[0])
+ for i in range(1,appends):
+ row[0].append("-")
+
+ if row[1] == True:
+ self.make_fields_unique(row[0])
+ table.field_names = row[0]
+ else:
+ table.add_row(row[0])
+ return table
+
+ def make_fields_unique(self, fields):
+ """
+ iterates over the row and make each field unique
+ """
+ for i in range(0, len(fields)):
+ for j in range(i+1, len(fields)):
+ if fields[i] == fields[j]:
+ fields[j] += "'"
+
+def from_html(html_code, **kwargs):
+ """
+ Generates a list of PrettyTables from a string of HTML code. Each <table> in
+ the HTML becomes one PrettyTable object.
+ """
+
+ parser = TableHandler(**kwargs)
+ parser.feed(html_code)
+ return parser.tables
+
+def from_html_one(html_code, **kwargs):
+ """
+ Generates a PrettyTables from a string of HTML code which contains only a
+ single <table>
+ """
+
+ tables = from_html(html_code, **kwargs)
+ try:
+ assert len(tables) == 1
+ except AssertionError:
+ raise Exception("More than one <table> in provided HTML code! Use from_html instead.")
+ return tables[0]
+
+##############################
+# MAIN (TEST FUNCTION) #
+##############################
+
+def main():
+
+ x = PrettyTable(["City name", "Area", "Population", "Annual Rainfall"])
+ x.sortby = "Population"
+ x.reversesort = True
+ x.int_format["Area"] = "04d"
+ x.float_format = "6.1f"
+ x.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 __name__ == "__main__":
+ main()