File: print_table.py

package info (click to toggle)
python-agate 1.9.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,996 kB
  • sloc: python: 8,512; makefile: 126
file content (156 lines) | stat: -rw-r--r-- 5,088 bytes parent folder | download | duplicates (2)
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
import math
import sys

from babel.numbers import format_decimal

from agate import config, utils
from agate.data_types import Number, Text


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 Flavored 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')
    truncation = config.get_option('text_truncation_chars')
    len_truncation = len(truncation)
    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%s' % (column_name[:max_column_width - len_truncation], truncation))
        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 = str(v)

            if max_column_width is not None and len(v) > max_column_width:
                v = '%s%s' % (v[:max_column_width - len_truncation], truncation)

            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(f'{v_line}{text}{v_line}')

    divider = '{v_line} {columns} {v_line}'.format(
        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])