1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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
48 FRAME = 0
49 ALL = 1
50 NONE = 2
51
52
53 DEFAULT = 10
54 MSWORD_FRIENDLY = 11
55 PLAIN_COLUMNS = 12
56 RANDOM = 20
57
68
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
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
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
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
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
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
181
184
185
186
187
188
189
190
191
192
193
194
195
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
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
227 try:
228 assert int(val) >= 0
229 except AssertionError:
230 raise Exception("Invalid value for %s: %s!" % (name, _unicode(val)))
231
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
246
259
261 try:
262 assert hasattr(val, "__call__")
263 except AssertionError:
264 raise Exception("Invalid value for %s! Must be a function." % name)
265
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
273 try:
274 assert val in self._field_names
275 except AssertionError:
276 raise Exception("Invalid field name: %s!" % val)
277
284
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
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
299
300
302 return self._field_names
303 """The names of the fields
304
305 Arguments:
306
307 fields - list or tuple of field names"""
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
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
331 return self._max_width
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
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
349 start = property(_get_start, _set_start)
350
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
361 end = property(_get_end, _set_end)
362
364 """Name of field by which to sort rows
365
366 Arguments:
367
368 sortby - field name to sort by"""
369 return self._sortby
373 sortby = property(_get_sortby, _set_sortby)
374
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
385 reversesort = property(_get_reversesort, _set_reversesort)
386
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
397 sort_key = property(_get_sort_key, _set_sort_key)
398
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
409 header = property(_get_header, _set_header)
410
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
421 border = property(_get_border, _set_border)
422
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
433 hrules = property(_get_hrules, _set_hrules)
434
445 int_format = property(_get_int_format, _set_int_format)
446
457 float_format = property(_get_float_format, _set_float_format)
458
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
469 padding_width = property(_get_padding_width, _set_padding_width)
470
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
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
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
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
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
505 vertical_char = property(_get_vertical_char, _set_vertical_char)
506
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
515 self._validate_option("horizontal_char", val)
516 self._horizontal_char = val
517 horizontal_char = property(_get_horizontal_char, _set_horizontal_char)
518
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
529 junction_char = property(_get_junction_char, _set_junction_char)
530
541 format = property(_get_format, _set_format)
542
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
551 self.validate_option("attributes", val)
552 self._attributes = val
553 attributes = property(_get_attributes, _set_attributes)
554
555
556
557
558
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
572
573
586
598
608
616
628
629
630
631
632
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
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
681
682 """Delete all rows from the table but keep the current field names"""
683
684 self._rows = []
685
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
696
697
699 return copy.deepcopy(self)
700
701
702
703
704
711
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
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
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
743 rows = copy.deepcopy(self._rows[options["start"]:options["end"]])
744
745 if options["sortby"]:
746 sortindex = self._field_names.index(options["sortby"])
747
748 rows = [[row[sortindex]]+row for row in rows]
749
750 rows.sort(reverse=options["reversesort"], key=options["sort_key"])
751
752 rows = [row[1:] for row in rows]
753 return rows
754
755
756
757
758
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
788 if self.rowcount == 0:
789 return ""
790
791 rows = self._get_rows(options)
792 self._compute_widths(rows, options)
793
794
795
796
797
798 rowbits = []
799 for row in rows:
800 rowbits.append(self._stringify_row(row, options))
801
802
803
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
810 bits.extend(rowbits)
811
812
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
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
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
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
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
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
931
932 return "\n".join(bits)
933
934
935
936
937
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
970
971 bits = []
972
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
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
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
1049
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"
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