Rosetta
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
prettytable.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 #
3 # Copyright (c) 2009, Luke Maurits <luke@maurits.id.au>
4 # All rights reserved.
5 # With contributions from:
6 # * Chris Clark
7 # * Klein Stephane
8 #
9 # Redistribution and use in source and binary forms, with or without
10 # modification, are permitted provided that the following conditions are met:
11 #
12 # * Redistributions of source code must retain the above copyright notice,
13 # this list of conditions and the following disclaimer.
14 # * Redistributions in binary form must reproduce the above copyright notice,
15 # this list of conditions and the following disclaimer in the documentation
16 # and/or other materials provided with the distribution.
17 # * The name of the author may not be used to endorse or promote products
18 # derived from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 # POSSIBILITY OF SUCH DAMAGE.
31 
32 __version__ = "0.6.1"
33 
34 import sys
35 import copy
36 import random
37 import textwrap
38 
39 py3k = sys.version_info[0] >= 3
40 if py3k:
41  unicode = str
42  basestring = str
43 if py3k and sys.version_info[1] >= 2:
44  from html import escape
45 else:
46  from cgi import escape
47 
48 # hrule styles
49 FRAME = 0
50 ALL = 1
51 NONE = 2
52 
53 # Table styles
54 DEFAULT = 10
55 MSWORD_FRIENDLY = 11
56 PLAIN_COLUMNS = 12
57 RANDOM = 20
58 
59 def _get_size(text):
60  max_width = 0
61  max_height = 0
62  text = _unicode(text)
63  for line in text.split("\n"):
64  max_height += 1
65  if len(line) > max_width:
66  max_width = len(line)
67 
68  return (max_width, max_height)
69 
70 def _unicode(value, encoding="UTF-8"):
71  if not isinstance(value, basestring):
72  value = str(value)
73  if not isinstance(value, unicode):
74  value = unicode(value, encoding, "replace")
75  return value
76 
77 class PrettyTable(object):
78 
79  def __init__(self, field_names=None, encoding="UTF-8", **kwargs):
80 
81  """Return a new PrettyTable instance
82 
83  Arguments:
84 
85  field_names - list or tuple of field names
86  encoding - Unicode encoding scheme to use
87  fields - list or tuple of field names to include in displays
88  start - index of first data row to include in output
89  end - index of last data row to include in output PLUS ONE (list slice style)
90  fields - names of fields (columns) to include
91  header - print a header showing field names (True or False)
92  border - print a border around the table (True or False)
93  hrules - controls printing of horizontal rules after rows. Allowed values: FRAME, ALL, NONE
94  int_format - controls formatting of integer data
95  float_format - controls formatting of floating point data
96  padding_width - number of spaces on either side of column data (only used if left and right paddings are None)
97  left_padding_width - number of spaces on left hand side of column data
98  right_padding_width - number of spaces on right hand side of column data
99  vertical_char - single character string used to draw vertical lines
100  horizontal_char - single character string used to draw horizontal lines
101  junction_char - single character string used to draw line junctions
102  sortby - name of field to sort rows by
103  sort_key - sorting key function, applied to data points before sorting
104  reversesort - True or False to sort in descending or ascending order"""
105 
106  self.encoding = encoding
107 
108  # Data
109  self._field_names = []
110  self._align = {}
111  self._max_width = {}
112  self._rows = []
113  if field_names:
114  self.field_names = field_names
115  else:
116  self._widths = []
117  self._rows = []
118 
119  # Options
120  self._options = "start end fields header border sortby reversesort sort_key attributes format hrules".split()
121  self._options.extend("int_format float_format padding_width left_padding_width right_padding_width".split())
122  self._options.extend("vertical_char horizontal_char junction_char".split())
123  for option in self._options:
124  if option in kwargs:
125  self._validate_option(option, kwargs[option])
126  else:
127  kwargs[option] = None
128 
129 
130  self._start = kwargs["start"] or 0
131  self._end = kwargs["end"] or None
132  self._fields = kwargs["fields"] or None
133 
134  self._header = kwargs["header"] or True
135  self._border = kwargs["border"] or True
136  self._hrules = kwargs["hrules"] or FRAME
137 
138  self._sortby = kwargs["sortby"] or None
139  self._reversesort = kwargs["reversesort"] or False
140  self._sort_key = kwargs["sort_key"] or (lambda x: x)
141 
142  self._int_format = kwargs["float_format"] or {}
143  self._float_format = kwargs["float_format"] or {}
144  self._padding_width = kwargs["padding_width"] or 1
145  self._left_padding_width = kwargs["left_padding_width"] or None
146  self._right_padding_width = kwargs["right_padding_width"] or None
147 
148  self._vertical_char = kwargs["vertical_char"] or "|"
149  self._horizontal_char = kwargs["horizontal_char"] or "-"
150  self._junction_char = kwargs["junction_char"] or "+"
151 
152  self._format = kwargs["format"] or False
153  self._attributes = kwargs["attributes"] or {}
154 
155  def __getattr__(self, name):
156 
157  if name == "rowcount":
158  return len(self._rows)
159  elif name == "colcount":
160  if self._field_names:
161  return len(self._field_names)
162  elif self._rows:
163  return len(self._rows[0])
164  else:
165  return 0
166  else:
167  raise AttributeError(name)
168 
169  def __getitem__(self, index):
170 
171  newtable = copy.deepcopy(self)
172  if isinstance(index, slice):
173  newtable._rows = self._rows[index]
174  elif isinstance(index, int):
175  newtable._rows = [self._rows[index],]
176  else:
177  raise Exception("Index %s is invalid, must be an integer or slice" % str(index))
178  return newtable
179 
180  def __str__(self):
181  if py3k:
182  return self.get_string()
183  else:
184  return self.get_string().encode(self.encoding)
185 
186  def __unicode__(self):
187  return self.get_string()
188 
189  ##############################
190  # ATTRIBUTE VALIDATORS #
191  ##############################
192 
193  # The method _validate_option is all that should be used elsewhere in the code base to validate options.
194  # It will call the appropriate validation method for that option. The individual validation methods should
195  # never need to be called directly (although nothing bad will happen if they *are*).
196  # Validation happens in TWO places.
197  # Firstly, in the property setters defined in the ATTRIBUTE MANAGMENT section.
198  # Secondly, in the _get_options method, where keyword arguments are mixed with persistent settings
199 
200  def _validate_option(self, option, val):
201  if option in ("start", "end", "padding_width", "left_padding_width", "right_padding_width", "format"):
202  self._validate_nonnegative_int(option, val)
203  elif option in ("sortby"):
204  self._validate_field_name(option, val)
205  elif option in ("sort_key"):
206  self._validate_function(option, val)
207  elif option in ("hrules"):
208  self._validate_hrules(option, val)
209  elif option in ("fields"):
210  self._validate_all_field_names(option, val)
211  elif option in ("header", "border", "reversesort"):
212  self._validate_true_or_false(option, val)
213  elif option in ("int_format"):
214  self._validate_int_format(option, val)
215  elif option in ("float_format"):
216  self._validate_float_format(option, val)
217  elif option in ("vertical_char", "horizontal_char", "junction_char"):
218  self._validate_single_char(option, val)
219  elif option in ("attributes"):
220  self._validate_attributes(option, val)
221  else:
222  raise Exception("Unrecognised option: %s!" % option)
223 
224  def _validate_align(self, val):
225  try:
226  assert val in ["l","c","r"]
227  except AssertionError:
228  raise Exception("Alignment %s is invalid, use l, c or r!" % val)
229 
230  def _validate_nonnegative_int(self, name, val):
231  try:
232  assert int(val) >= 0
233  except AssertionError:
234  raise Exception("Invalid value for %s: %s!" % (name, _unicode(val)))
235 
236  def _validate_true_or_false(self, name, val):
237  try:
238  assert val in (True, False)
239  except AssertionError:
240  raise Exception("Invalid value for %s! Must be True or False." % name)
241 
242  def _validate_int_format(self, name, val):
243  if val == "":
244  return
245  try:
246  assert type(val) in (str, unicode)
247  assert val.isdigit()
248  except AssertionError:
249  raise Exception("Invalid value for %s! Must be an integer format string." % name)
250 
251  def _validate_float_format(self, name, val):
252  if val == "":
253  return
254  try:
255  assert type(val) in (str, unicode)
256  assert "." in val
257  bits = val.split(".")
258  assert len(bits) <= 2
259  assert bits[0] == "" or bits[0].isdigit()
260  assert bits[1] == "" or bits[1].isdigit()
261  except AssertionError:
262  raise Exception("Invalid value for %s! Must be a float format string." % name)
263 
264  def _validate_function(self, name, val):
265  try:
266  assert hasattr(val, "__call__")
267  except AssertionError:
268  raise Exception("Invalid value for %s! Must be a function." % name)
269 
270  def _validate_hrules(self, name, val):
271  try:
272  assert val in (ALL, FRAME, NONE)
273  except AssertionError:
274  raise Exception("Invalid value for %s! Must be ALL, FRAME or NONE." % name)
275 
276  def _validate_field_name(self, name, val):
277  try:
278  assert val in self._field_names
279  except AssertionError:
280  raise Exception("Invalid field name: %s!" % val)
281 
282  def _validate_all_field_names(self, name, val):
283  try:
284  for x in val:
285  self._validate_field_name(name, x)
286  except AssertionError:
287  raise Exception("fields must be a sequence of field names!")
288 
289  def _validate_single_char(self, name, val):
290  try:
291  assert len(_unicode(val)) == 1
292  except AssertionError:
293  raise Exception("Invalid value for %s! Must be a string of length 1." % name)
294 
295  def _validate_attributes(self, name, val):
296  try:
297  assert isinstance(val, dict)
298  except AssertionError:
299  raise Exception("attributes must be a dictionary of name/value pairs!")
300 
301  ##############################
302  # ATTRIBUTE MANAGEMENT #
303  ##############################
304 
305  def _get_field_names(self):
306  return self._field_names
307  """The names of the fields
308 
309  Arguments:
310 
311  fields - list or tuple of field names"""
312  def _set_field_names(self, val):
313  if self._field_names:
314  old_names = self._field_names[:]
315  self._field_names = val
316  if self._align and old_names:
317  for old_name, new_name in zip(old_names, val):
318  self._align[new_name] = self._align[old_name]
319  for old_name in old_names:
320  self._align.pop(old_name)
321  else:
322  for field in self._field_names:
323  self._align[field] = "c"
324  field_names = property(_get_field_names, _set_field_names)
325 
326  def _get_align(self):
327  return self._align
328  def _set_align(self, val):
329  self._validate_align(val)
330  for field in self._field_names:
331  self._align[field] = val
332  align = property(_get_align, _set_align)
333 
334  def _get_max_width(self):
335  return self._max_width
336  def _set_max_width(self, val):
337  self._validate_nonnegativeint(val)
338  for field in self._field_names:
339  self._max_width[field] = val
340  max_width = property(_get_max_width, _set_max_width)
341 
342  def _get_start(self):
343  """Start index of the range of rows to print
344 
345  Arguments:
346 
347  start - index of first data row to include in output"""
348  return self._start
349 
350  def _set_start(self, val):
351  self._validate_option("start", val)
352  self._start = val
353  start = property(_get_start, _set_start)
354 
355  def _get_end(self):
356  """End index of the range of rows to print
357 
358  Arguments:
359 
360  end - index of last data row to include in output PLUS ONE (list slice style)"""
361  return self._end
362  def _set_end(self, val):
363  self._validate_option("end", val)
364  self._end = val
365  end = property(_get_end, _set_end)
366 
367  def _get_sortby(self):
368  """Name of field by which to sort rows
369 
370  Arguments:
371 
372  sortby - field name to sort by"""
373  return self._sortby
374  def _set_sortby(self, val):
375  self._validate_option("sortby", val)
376  self._sortby = val
377  sortby = property(_get_sortby, _set_sortby)
378 
379  def _get_reversesort(self):
380  """Controls direction of sorting (ascending vs descending)
381 
382  Arguments:
383 
384  reveresort - set to True to sort by descending order, or False to sort by ascending order"""
385  return self._reversesort
386  def _set_reversesort(self, val):
387  self._validate_option("reversesort", val)
388  self._reversesort = val
389  reversesort = property(_get_reversesort, _set_reversesort)
390 
391  def _get_sort_key(self):
392  """Sorting key function, applied to data points before sorting
393 
394  Arguments:
395 
396  sort_key - a function which takes one argument and returns something to be sorted"""
397  return self._sort_key
398  def _set_sort_key(self, val):
399  self._validate_option("sort_key", val)
400  self._sort_key = val
401  sort_key = property(_get_sort_key, _set_sort_key)
402 
403  def _get_header(self):
404  """Controls printing of table header with field names
405 
406  Arguments:
407 
408  header - print a header showing field names (True or False)"""
409  return self._header
410  def _set_header(self, val):
411  self._validate_option("header", val)
412  self._header = val
413  header = property(_get_header, _set_header)
414 
415  def _get_border(self):
416  """Controls printing of border around table
417 
418  Arguments:
419 
420  border - print a border around the table (True or False)"""
421  return self._border
422  def _set_border(self, val):
423  self._validate_option("border", val)
424  self._border = val
425  border = property(_get_border, _set_border)
426 
427  def _get_hrules(self):
428  """Controls printing of horizontal rules after rows
429 
430  Arguments:
431 
432  hrules - horizontal rules style. Allowed values: FRAME, ALL, NONE"""
433  return self._hrules
434  def _set_hrules(self, val):
435  self._validate_option("hrules", val)
436  self._hrules = val
437  hrules = property(_get_hrules, _set_hrules)
438 
439  def _get_int_format(self):
440  """Controls formatting of integer data
441  Arguments:
442 
443  int_format - integer format string"""
444  return self._int_format
445  def _set_int_format(self, val):
446  self._validate_option("int_format", val)
447  for field in self._field_names:
448  self._int_format[field] = val
449  int_format = property(_get_int_format, _set_int_format)
450 
451  def _get_float_format(self):
452  """Controls formatting of floating point data
453  Arguments:
454 
455  float_format - floating point format string"""
456  return self._float_format
457  def _set_float_format(self, val):
458  self._validate_option("float_format", val)
459  for field in self._field_names:
460  self._float_format[field] = val
461  float_format = property(_get_float_format, _set_float_format)
462 
464  """The number of empty spaces between a column's edge and its content
465 
466  Arguments:
467 
468  padding_width - number of spaces, must be a positive integer"""
469  return self._padding_width
470  def _set_padding_width(self, val):
471  self._validate_option("padding_width", val)
472  self._padding_width = val
473  padding_width = property(_get_padding_width, _set_padding_width)
474 
476  """The number of empty spaces between a column's left edge and its content
477 
478  Arguments:
479 
480  left_padding - number of spaces, must be a positive integer"""
481  return self._left_padding_width
482  def _set_left_padding_width(self, val):
483  self._validate_option("left_padding_width", val)
484  self._left_padding_width = val
485  left_padding_width = property(_get_left_padding_width, _set_left_padding_width)
486 
488  """The number of empty spaces between a column's right edge and its content
489 
490  Arguments:
491 
492  right_padding - number of spaces, must be a positive integer"""
493  return self._right_padding_width
494  def _set_right_padding_width(self, val):
495  self._validate_option("right_padding_width", val)
496  self._right_padding_width = val
497  right_padding_width = property(_get_right_padding_width, _set_right_padding_width)
498 
500  """The charcter used when printing table borders to draw vertical lines
501 
502  Arguments:
503 
504  vertical_char - single character string used to draw vertical lines"""
505  return self._vertical_char
506  def _set_vertical_char(self, val):
507  self._validate_option("vertical_char", val)
508  self._vertical_char = val
509  vertical_char = property(_get_vertical_char, _set_vertical_char)
510 
512  """The charcter used when printing table borders to draw horizontal lines
513 
514  Arguments:
515 
516  horizontal_char - single character string used to draw horizontal lines"""
517  return self._horizontal_char
518  def _set_horizontal_char(self, val):
519  self._validate_option("horizontal_char", val)
520  self._horizontal_char = val
521  horizontal_char = property(_get_horizontal_char, _set_horizontal_char)
522 
524  """The charcter used when printing table borders to draw line junctions
525 
526  Arguments:
527 
528  junction_char - single character string used to draw line junctions"""
529  return self._junction_char
530  def _set_junction_char(self, val):
531  self._validate_option("vertical_char", val)
532  self._junction_char = val
533  junction_char = property(_get_junction_char, _set_junction_char)
534 
535  def _get_format(self):
536  """Controls whether or not HTML tables are formatted to match styling options
537 
538  Arguments:
539 
540  format - True or False"""
541  return self._format
542  def _set_format(self, val):
543  self._validate_option("format", val)
544  self._format = val
545  format = property(_get_format, _set_format)
546 
547  def _get_attributes(self):
548  """A dictionary of HTML attribute name/value pairs to be included in the <table> tag when printing HTML
549 
550  Arguments:
551 
552  attributes - dictionary of attributes"""
553  return self._attributes
554  def _set_attributes(self, val):
555  self.validate_option("attributes", val)
556  self._attributes = val
557  attributes = property(_get_attributes, _set_attributes)
558 
559  ##############################
560  # OPTION MIXER #
561  ##############################
562 
563  def _get_options(self, kwargs):
564 
565  options = {}
566  for option in self._options:
567  if option in kwargs:
568  self._validate_option(option, kwargs[option])
569  options[option] = kwargs[option]
570  else:
571  options[option] = getattr(self, "_"+option)
572  return options
573 
574  ##############################
575  # PRESET STYLE LOGIC #
576  ##############################
577 
578  def set_style(self, style):
579 
580  if style == DEFAULT:
581  self._set_default_style()
582  elif style == MSWORD_FRIENDLY:
583  self._set_msword_style()
584  elif style == PLAIN_COLUMNS:
585  self._set_columns_style()
586  elif style == RANDOM:
587  self._set_random_style()
588  else:
589  raise Exception("Invalid pre-set style!")
590 
592 
593  self.header = True
594  self.border = True
595  self._hrules = FRAME
596  self.padding_width = 1
597  self.left_padding_width = 1
598  self.right_padding_width = 1
599  self.vertical_char = "|"
600  self.horizontal_char = "-"
601  self.junction_char = "+"
602 
603  def _set_msword_style(self):
604 
605  self.header = True
606  self.border = True
607  self._hrules = NONE
608  self.padding_width = 1
609  self.left_padding_width = 1
610  self.right_padding_width = 1
611  self.vertical_char = "|"
612 
614 
615  self.header = True
616  self.border = False
617  self.padding_width = 1
618  self.left_padding_width = 0
619  self.right_padding_width = 8
620 
621  def _set_random_style(self):
622 
623  # Just for fun!
624  self.header = random.choice((True, False))
625  self.border = random.choice((True, False))
626  self._hrules = random.choice((ALL, FRAME, NONE))
627  self.left_padding_width = random.randint(0,5)
628  self.right_padding_width = random.randint(0,5)
629  self.vertical_char = random.choice("~!@#$%^&*()_+|-=\{}[];':\",./;<>?")
630  self.horizontal_char = random.choice("~!@#$%^&*()_+|-=\{}[];':\",./;<>?")
631  self.junction_char = random.choice("~!@#$%^&*()_+|-=\{}[];':\",./;<>?")
632 
633  ##############################
634  # DATA INPUT METHODS #
635  ##############################
636 
637  def add_row(self, row):
638 
639  """Add a row to the table
640 
641  Arguments:
642 
643  row - row of data, should be a list with as many elements as the table
644  has fields"""
645 
646  if self._field_names and len(row) != len(self._field_names):
647  raise Exception("Row has incorrect number of values, (actual) %d!=%d (expected)" %(len(row),len(self._field_names)))
648  self._rows.append(list(row))
649 
650  def del_row(self, row_index):
651 
652  """Delete a row to the table
653 
654  Arguments:
655 
656  row_index - The index of the row you want to delete. Indexing starts at 0."""
657 
658  if row_index > len(self._rows)-1:
659  raise Exception("Cant delete row at index %d, table only has %d rows!" % (row_index, len(self._rows)))
660  del self._rows[row_index]
661 
662  def add_column(self, fieldname, column, align="c"):
663 
664  """Add a column to the table.
665 
666  Arguments:
667 
668  fieldname - name of the field to contain the new column of data
669  column - column of data, should be a list with as many elements as the
670  table has rows
671  align - desired alignment for this column - "l" for left, "c" for centre and "r" for right"""
672 
673  if len(self._rows) in (0, len(column)):
674  self._validate_align(align)
675  self._field_names.append(fieldname)
676  self._align[fieldname] = align
677  for i in range(0, len(column)):
678  if len(self._rows) < i+1:
679  self._rows.append([])
680  self._rows[i].append(column[i])
681  else:
682  raise Exception("Column length %d does not match number of rows %d!" % (len(column), len(self._rows)))
683 
684  def clear_rows(self):
685 
686  """Delete all rows from the table but keep the current field names"""
687 
688  self._rows = []
689 
690  def clear(self):
691 
692  """Delete all rows and field names from the table, maintaining nothing but styling options"""
693 
694  self._rows = []
695  self._field_names = []
696  self._widths = []
697 
698  ##############################
699  # MISC PUBLIC METHODS #
700  ##############################
701 
702  def copy(self):
703  return copy.deepcopy(self)
704 
705  ##############################
706  # MISC PRIVATE METHODS #
707  ##############################
708 
709  def _format_value(self, field, value):
710  if isinstance(value, int) and field in self._int_format:
711  value = ("%%%sd" % self._int_format[field]) % value
712  elif isinstance(value, float) and field in self._float_format:
713  value = ("%%%sf" % self._float_format[field]) % value
714  return value
715 
716  def _compute_widths(self, rows, options):
717  if options["header"]:
718  widths = [_get_size(field)[0] for field in self._field_names]
719  else:
720  widths = len(self.field_names) * [0]
721  for row in rows:
722  for index, value in enumerate(row):
723  value = self._format_value(self.field_names[index], value)
724  widths[index] = max(widths[index], _get_size(_unicode(value))[0])
725  self._widths = widths
726 
727  def _get_padding_widths(self, options):
728 
729  if options["left_padding_width"] is not None:
730  lpad = options["left_padding_width"]
731  else:
732  lpad = options["padding_width"]
733  if options["right_padding_width"] is not None:
734  rpad = options["right_padding_width"]
735  else:
736  rpad = options["padding_width"]
737  return lpad, rpad
738 
739  def _get_rows(self, options):
740  """Return only those data rows that should be printed, based on slicing and sorting.
741 
742  Arguments:
743 
744  options - dictionary of option settings."""
745 
746  # Make a copy of only those rows in the slice range
747  rows = copy.deepcopy(self._rows[options["start"]:options["end"]])
748  # Sort if necessary
749  if options["sortby"]:
750  sortindex = self._field_names.index(options["sortby"])
751  # Decorate
752  rows = [[row[sortindex]]+row for row in rows]
753  # Sort
754  rows.sort(reverse=options["reversesort"], key=options["sort_key"])
755  # Undecorate
756  rows = [row[1:] for row in rows]
757  return rows
758 
759  ##############################
760  # PLAIN TEXT STRING METHODS #
761  ##############################
762 
763  def get_string(self, **kwargs):
764 
765  """Return string representation of table in current state.
766 
767  Arguments:
768 
769  start - index of first data row to include in output
770  end - index of last data row to include in output PLUS ONE (list slice style)
771  fields - names of fields (columns) to include
772  header - print a header showing field names (True or False)
773  border - print a border around the table (True or False)
774  hrules - controls printing of horizontal rules after rows. Allowed values: FRAME, ALL, NONE
775  int_format - controls formatting of integer data
776  float_format - controls formatting of floating point data
777  padding_width - number of spaces on either side of column data (only used if left and right paddings are None)
778  left_padding_width - number of spaces on left hand side of column data
779  right_padding_width - number of spaces on right hand side of column data
780  vertical_char - single character string used to draw vertical lines
781  horizontal_char - single character string used to draw horizontal lines
782  junction_char - single character string used to draw line junctions
783  sortby - name of field to sort rows by
784  sort_key - sorting key function, applied to data points before sorting
785  reversesort - True or False to sort in descending or ascending order"""
786 
787  options = self._get_options(kwargs)
788 
789  bits = []
790 
791  # Don't think too hard about an empty table
792  if self.rowcount == 0:
793  return ""
794 
795  rows = self._get_rows(options)
796  self._compute_widths(rows, options)
797 
798  # Build rows
799  # (for now, this is done before building headers etc. because rowbits.append
800  # contains width-adjusting voodoo which has to be done first. This is ugly
801  # and Wrong and will change soon)
802  rowbits = []
803  for row in rows:
804  rowbits.append(self._stringify_row(row, options))
805 
806 
807  # Add header or top of border
808  if options["header"]:
809  bits.append(self._stringify_header(options))
810  elif options["border"] and options["hrules"] != NONE:
811  bits.append(self._hrule)
812 
813  # Add rows
814  bits.extend(rowbits)
815 
816  # Add bottom of border
817  if options["border"] and not options["hrules"]:
818  bits.append(self._hrule)
819 
820  string = "\n".join(bits)
821  self._nonunicode = string
822  return _unicode(string)
823 
824  def _stringify_hrule(self, options):
825 
826  if not options["border"]:
827  return ""
828  lpad, rpad = self._get_padding_widths(options)
829  bits = [options["junction_char"]]
830  for field, width in zip(self._field_names, self._widths):
831  if options["fields"] and field not in options["fields"]:
832  continue
833  bits.append((width+lpad+rpad)*options["horizontal_char"])
834  bits.append(options["junction_char"])
835  return "".join(bits)
836 
837  def _stringify_header(self, options):
838 
839  bits = []
840  lpad, rpad = self._get_padding_widths(options)
841  if options["border"]:
842  if options["hrules"] != NONE:
843  bits.append(self._hrule)
844  bits.append("\n")
845  bits.append(options["vertical_char"])
846  for field, width, in zip(self._field_names, self._widths):
847  if options["fields"] and field not in options["fields"]:
848  continue
849  if self._align[field] == "l":
850  bits.append(" " * lpad + _unicode(field).ljust(width) + " " * rpad)
851  elif self._align[field] == "r":
852  bits.append(" " * lpad + _unicode(field).rjust(width) + " " * rpad)
853  else:
854  bits.append(" " * lpad + _unicode(field).center(width) + " " * rpad)
855  if options["border"]:
856  bits.append(options["vertical_char"])
857  if options["border"] and options["hrules"] != NONE:
858  bits.append("\n")
859  bits.append(self._hrule)
860  return "".join(bits)
861 
862  def _stringify_row(self, row, options):
863 
864  for index, value in enumerate(row):
865  row[index] = self._format_value(self.field_names[index], value)
866 
867  for index, field, value, width, in zip(range(0,len(row)), self._field_names, row, self._widths):
868  # Enforce max widths
869  max_width = self._max_width.get(field, 0)
870  lines = _unicode(value).split("\n")
871  new_lines = []
872  for line in lines:
873  if max_width and len(line) > max_width:
874  line = textwrap.fill(line, max_width)
875  new_lines.append(line)
876  lines = new_lines
877  value = "\n".join(lines)
878  row[index] = value
879 
880  #old_widths = self._widths[:]
881 
882  for index, field in enumerate(self._field_names):
883  namewidth = len(field)
884  datawidth = min(self._widths[index], self._max_width.get(field, self._widths[index]))
885  if options["header"]:
886  self._widths[index] = max(namewidth, datawidth)
887  else:
888  self._widths[index] = datawidth
889 
890  row_height = 0
891  for c in row:
892  h = _get_size(c)[1]
893  if h > row_height:
894  row_height = h
895 
896  bits = []
897  lpad, rpad = self._get_padding_widths(options)
898  for y in range(0, row_height):
899  bits.append([])
900  if options["border"]:
901  bits[y].append(self.vertical_char)
902 
903  for field, value, width, in zip(self._field_names, row, self._widths):
904 
905  lines = _unicode(value).split("\n")
906  if len(lines) < row_height:
907  lines = lines + ([""] * (row_height-len(lines)))
908 
909  y = 0
910  for l in lines:
911  if options["fields"] and field not in options["fields"]:
912  continue
913 
914  if self._align[field] == "l":
915  bits[y].append(" " * lpad + _unicode(l).ljust(width) + " " * rpad)
916  elif self._align[field] == "r":
917  bits[y].append(" " * lpad + _unicode(l).rjust(width) + " " * rpad)
918  else:
919  bits[y].append(" " * lpad + _unicode(l).center(width) + " " * rpad)
920  if options["border"]:
921  bits[y].append(self.vertical_char)
922 
923  y += 1
924 
925  self._hrule = self._stringify_hrule(options)
926 
927  if options["border"] and options["hrules"]== ALL:
928  bits[row_height-1].append("\n")
929  bits[row_height-1].append(self._hrule)
930 
931  for y in range(0, row_height):
932  bits[y] = "".join(bits[y])
933 
934  #self._widths = old_widths
935 
936  return "\n".join(bits)
937 
938  ##############################
939  # HTML STRING METHODS #
940  ##############################
941 
942  def get_html_string(self, **kwargs):
943 
944  """Return string representation of HTML formatted version of table in current state.
945 
946  Arguments:
947 
948  start - index of first data row to include in output
949  end - index of last data row to include in output PLUS ONE (list slice style)
950  fields - names of fields (columns) to include
951  header - print a header showing field names (True or False)
952  border - print a border around the table (True or False)
953  hrules - controls printing of horizontal rules after rows. Allowed values: FRAME, ALL, NONE
954  int_format - controls formatting of integer data
955  float_format - controls formatting of floating point data
956  padding_width - number of spaces on either side of column data (only used if left and right paddings are None)
957  left_padding_width - number of spaces on left hand side of column data
958  right_padding_width - number of spaces on right hand side of column data
959  sortby - name of field to sort rows by
960  sort_key - sorting key function, applied to data points before sorting
961  attributes - dictionary of name/value pairs to include as HTML attributes in the <table> tag"""
962 
963  options = self._get_options(kwargs)
964 
965  if options["format"]:
966  string = self._get_formatted_html_string(options)
967  else:
968  string = self._get_simple_html_string(options)
969 
970  self._nonunicode = string
971  return _unicode(string)
972 
973  def _get_simple_html_string(self, options):
974 
975  bits = []
976  # Slow but works
977  table_tag = '<table'
978  if options["border"]:
979  table_tag += ' border="1"'
980  if options["attributes"]:
981  for attr_name in options["attributes"]:
982  table_tag += ' %s="%s"' % (attr_name, options["attributes"][attr_name])
983  table_tag += '>'
984  bits.append(table_tag)
985 
986  # Headers
987  if options["header"]:
988  bits.append(" <tr>")
989  for field in self._field_names:
990  if options["fields"] and field not in options["fields"]:
991  continue
992  bits.append(" <th>%s</th>" % escape(_unicode(field)).replace("\n", "<br />"))
993  bits.append(" </tr>")
994 
995  # Data
996  rows = self._get_rows(options)
997  for row in rows:
998  bits.append(" <tr>")
999  for field, datum in zip(self._field_names, row):
1000  if options["fields"] and field not in options["fields"]:
1001  continue
1002  bits.append(" <td>%s</td>" % escape(_unicode(datum)).replace("\n", "<br />"))
1003  bits.append(" </tr>")
1004 
1005  bits.append("</table>")
1006  string = "\n".join(bits)
1007 
1008  self._nonunicode = string
1009  return _unicode(string)
1010 
1011  def _get_formatted_html_string(self, options):
1012 
1013  bits = []
1014  lpad, rpad = self._get_padding_widths(options)
1015  # Slow but works
1016  table_tag = '<table'
1017  if options["border"]:
1018  table_tag += ' border="1"'
1019  if options["hrules"] == NONE:
1020  table_tag += ' frame="vsides" rules="cols"'
1021  if options["attributes"]:
1022  for attr_name in options["attributes"]:
1023  table_tag += ' %s="%s"' % (attr_name, options["attributes"][attr_name])
1024  table_tag += '>'
1025  bits.append(table_tag)
1026  # Headers
1027  if options["header"]:
1028  bits.append(" <tr>")
1029  for field in self._field_names:
1030  if options["fields"] and field not in options["fields"]:
1031  continue
1032  bits.append(" <th style=\"padding-left: %dem; padding-right: %dem; text-align: center\">%s</th>" % (lpad, rpad, escape(_unicode(field)).replace("\n", "<br />")))
1033  bits.append(" </tr>")
1034  # Data
1035  rows = self._get_rows(options)
1036  for row in self._rows:
1037  bits.append(" <tr>")
1038  for field, datum in zip(self._field_names, row):
1039  if options["fields"] and field not in options["fields"]:
1040  continue
1041  if self._align[field] == "l":
1042  bits.append(" <td style=\"padding-left: %dem; padding-right: %dem; text-align: left\">%s</td>" % (lpad, rpad, escape(_unicode(datum)).replace("\n", "<br />")))
1043  elif self._align[field] == "r":
1044  bits.append(" <td style=\"padding-left: %dem; padding-right: %dem; text-align: right\">%s</td>" % (lpad, rpad, escape(_unicode(datum)).replace("\n", "<br />")))
1045  else:
1046  bits.append(" <td style=\"padding-left: %dem; padding-right: %dem; text-align: center\">%s</td>" % (lpad, rpad, escape(_unicode(datum)).replace("\n", "<br />")))
1047  bits.append(" </tr>")
1048  bits.append("</table>")
1049  string = "\n".join(bits)
1050 
1051  self._nonunicode = string
1052  return _unicode(string)
1053 
1054 def main():
1055 
1056  x = PrettyTable(["City name", "Area", "Population", "Annual Rainfall"])
1057  x.sortby = "Population"
1058  x.reversesort = True
1059  x.int_format["Area"] = "04"
1060  x.float_format = "6.1"
1061  x.align["City name"] = "l" # Left align city names
1062  x.add_row(["Adelaide", 1295, 1158259, 600.5])
1063  x.add_row(["Brisbane", 5905, 1857594, 1146.4])
1064  x.add_row(["Darwin", 112, 120900, 1714.7])
1065  x.add_row(["Hobart", 1357, 205556, 619.5])
1066  x.add_row(["Sydney", 2058, 4336374, 1214.8])
1067  x.add_row(["Melbourne", 1566, 3806092, 646.9])
1068  x.add_row(["Perth", 5386, 1554769, 869.4])
1069  print(x)
1070 
1071 if __name__ == "__main__":
1072  main()
static T min(T x, T y)
Definition: Svm.cc:16
xyzTriple< T > center(xyzTriple< T > const &a, xyzTriple< T > const &b)
Center of 2 xyzTriples.
Fstring::size_type len(Fstring const &s)
Length.
Definition: Fstring.hh:2207
utility::vector1< std::string > split(const std::string &s)
split given std::string using ' ' symbol.
Definition: string_util.cc:59
Fstring ljust(Fstring const &s)
Left-Justified Copy.
Definition: Fstring.hh:2404
Fstring rjust(Fstring const &s)
Right-Justified Copy.
Definition: Fstring.hh:2413
static T max(T x, T y)
Definition: Svm.cc:19