-
Notifications
You must be signed in to change notification settings - Fork 153
/
print_table.py
161 lines (128 loc) · 5.04 KB
/
print_table.py
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#!/usr/bin/env python
# pylint: disable=W0212
import math
import sys
from babel.numbers import format_decimal
import six
from agate import config
from agate.data_types import Number, Text
from agate import utils
def print_table(self, max_rows=20, max_columns=6, output=sys.stdout, max_column_width=20, locale=None, max_precision=3):
"""
Print a text-based view of the data in this table.
The output of this method is Github Friendly Markdown (GFM) compatible.
:param max_rows:
The maximum number of rows to display before truncating the data. This
defaults to :code:`20` to prevent accidental printing of the entire
table. Pass :code:`None` to disable the limit.
:param max_columns:
The maximum number of columns to display before truncating the data.
This defaults to :code:`6` to prevent wrapping in most cases. Pass
:code:`None` to disable the limit.
:param output:
A file-like object to print to.
:param max_column_width:
Truncate all columns to at most this width. The remainder will be
replaced with ellipsis.
:param locale:
Provide a locale you would like to be used to format the output.
By default it will use the system's setting.
:max_precision:
Puts a limit on the maximum precision displayed for number types.
Numbers with lesser precision won't be affected.
This defaults to :code:`3`. Pass :code:`None` to disable limit.
"""
if max_rows is None:
max_rows = len(self._rows)
if max_columns is None:
max_columns = len(self._columns)
if max_precision is None:
max_precision = float('inf')
ellipsis = config.get_option('ellipsis_chars')
h_line = config.get_option('horizontal_line_char')
v_line = config.get_option('vertical_line_char')
locale = locale or config.get_option('default_locale')
rows_truncated = max_rows < len(self._rows)
columns_truncated = max_columns < len(self._column_names)
column_names = []
for column_name in self.column_names[:max_columns]:
if max_column_width is not None and len(column_name) > max_column_width:
column_names.append('%s...' % column_name[:max_column_width - 3])
else:
column_names.append(column_name)
if columns_truncated:
column_names.append(ellipsis)
widths = [len(n) for n in column_names]
number_formatters = []
formatted_data = []
# Determine correct number of decimal places for each Number column
for i, c in enumerate(self._columns):
if i >= max_columns:
break
if isinstance(c.data_type, Number):
max_places = utils.max_precision(c[:max_rows])
add_ellipsis = False
if max_places > max_precision:
add_ellipsis = True
max_places = max_precision
number_formatters.append(utils.make_number_formatter(max_places, add_ellipsis))
else:
number_formatters.append(None)
# Format data and display column widths
for i, row in enumerate(self._rows):
if i >= max_rows:
break
formatted_row = []
for j, v in enumerate(row):
if j >= max_columns:
v = ellipsis
elif v is None:
v = ''
elif number_formatters[j] is not None and not math.isinf(v):
v = format_decimal(
v,
format=number_formatters[j],
locale=locale
)
else:
v = six.text_type(v)
if max_column_width is not None and len(v) > max_column_width:
v = '%s...' % v[:max_column_width - 3]
if len(v) > widths[j]:
widths[j] = len(v)
formatted_row.append(v)
if j >= max_columns:
break
formatted_data.append(formatted_row)
def write(line):
output.write(line + '\n')
def write_row(formatted_row):
"""
Helper function that formats individual rows.
"""
row_output = []
for j, d in enumerate(formatted_row):
# Text is left-justified, all other values are right-justified
if isinstance(self._column_types[j], Text):
output = ' %s ' % d.ljust(widths[j])
else:
output = ' %s ' % d.rjust(widths[j])
row_output.append(output)
text = v_line.join(row_output)
write('%s%s%s' % (v_line, text, v_line))
# Dashes span each width with '+' character at intersection of
# horizontal and vertical dividers.
divider = '%(v_line)s %(columns)s %(v_line)s' % {
'h_line': h_line,
'v_line': v_line,
'columns': ' | '.join(h_line * w for w in widths)
}
# Headers
write_row(column_names)
write(divider)
# Rows
for formatted_row in formatted_data:
write_row(formatted_row)
# Row indicating data was truncated
if rows_truncated:
write_row([ellipsis for n in column_names])