Package cm_shell :: Module prettytable
[hide private]
[frames] | no frames]

Source Code for Module cm_shell.prettytable

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