161 lines
5.0 KiB
Python
161 lines
5.0 KiB
Python
|
#!/usr/bin/env python
|
||
|
# pylint: disable=W0212
|
||
|
|
||
|
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:
|
||
|
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])
|