terminaltables3 4.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,17 @@
1
+ """Generate simple tables in terminals from a nested list of strings.
2
+
3
+ Use SingleTable or DoubleTable instead of AsciiTable for box-drawing characters.
4
+
5
+ https://github.com/Robpol86/terminaltables3
6
+ https://pypi.python.org/pypi/terminaltables3
7
+ """
8
+
9
+ from terminaltables3.ascii_table import AsciiTable # noqa
10
+ from terminaltables3.github_table import GithubFlavoredMarkdownTable # noqa
11
+ from terminaltables3.other_tables import DoubleTable # noqa
12
+ from terminaltables3.other_tables import PorcelainTable # noqa
13
+ from terminaltables3.other_tables import SingleTable # noqa
14
+
15
+ __author__ = "@Robpol86"
16
+ __license__ = "MIT"
17
+ __version__ = "3.1.0"
@@ -0,0 +1,63 @@
1
+ """AsciiTable is the main table class. To be inherited by other tables. Define convenience methods here."""
2
+
3
+ from terminaltables3.base_table import BaseTable
4
+ from terminaltables3.terminal_io import terminal_size
5
+ from terminaltables3.width_and_alignment import (
6
+ column_max_width,
7
+ max_dimensions,
8
+ table_width,
9
+ )
10
+
11
+
12
+ class AsciiTable(BaseTable):
13
+ """Draw a table using regular ASCII characters, such as ``+``, ``|``, and ``-``.
14
+
15
+ :ivar iter table_data: List (empty or list of lists of strings) representing the table.
16
+ :ivar str title: Optional title to show within the top border of the table.
17
+ :ivar bool inner_column_border: Separates columns.
18
+ :ivar bool inner_footing_row_border: Show a border before the last row.
19
+ :ivar bool inner_heading_row_border: Show a border after the first row.
20
+ :ivar bool inner_row_border: Show a border in between every row.
21
+ :ivar bool outer_border: Show the top, left, right, and bottom border.
22
+ :ivar dict justify_columns: Horizontal justification. Keys are column indexes (int). Values are right/left/center.
23
+ :ivar int padding_left: Number of spaces to pad on the left side of every cell.
24
+ :ivar int padding_right: Number of spaces to pad on the right side of every cell.
25
+ """
26
+
27
+ def column_max_width(self, column_number: int) -> int:
28
+ """Return the maximum width of a column based on the current terminal width.
29
+
30
+ :param int column_number: The column number to query.
31
+
32
+ :return: The max width of the column.
33
+ :rtype: int
34
+ """
35
+ inner_widths = max_dimensions(self.table_data)[0]
36
+ outer_border = 2 if self.outer_border else 0
37
+ inner_border = 1 if self.inner_column_border else 0
38
+ padding = self.padding_left + self.padding_right
39
+ return column_max_width(
40
+ inner_widths, column_number, outer_border, inner_border, padding
41
+ )
42
+
43
+ @property
44
+ def column_widths(self) -> list[int]:
45
+ """Return a list of integers representing the widths of each table column without padding."""
46
+ if not self.table_data:
47
+ return []
48
+ return max_dimensions(self.table_data)[0]
49
+
50
+ @property
51
+ def ok(self) -> bool: # Too late to change API. # pylint: disable=invalid-name
52
+ """Return True if the table fits within the terminal width, False if the table breaks."""
53
+ return self.table_width <= terminal_size()[0]
54
+
55
+ @property
56
+ def table_width(self) -> int:
57
+ """Return the width of the table including padding and borders."""
58
+ outer_widths = max_dimensions(
59
+ self.table_data, self.padding_left, self.padding_right
60
+ )[2]
61
+ outer_border = 2 if self.outer_border else 0
62
+ inner_border = 1 if self.inner_column_border else 0
63
+ return table_width(outer_widths, outer_border, inner_border)
@@ -0,0 +1,236 @@
1
+ """Base table class. Define just the bare minimum to build tables."""
2
+
3
+ from typing import Generator, Optional, Sequence, Tuple
4
+
5
+ from terminaltables3.build import build_border, build_row, flatten
6
+ from terminaltables3.width_and_alignment import align_and_pad_cell, max_dimensions
7
+
8
+
9
+ class BaseTable:
10
+ """Base table class.
11
+
12
+ :ivar iter table_data: List (empty or list of lists of strings) representing the table.
13
+ :ivar str title: Optional title to show within the top border of the table.
14
+ :ivar bool inner_column_border: Separates columns.
15
+ :ivar bool inner_footing_row_border: Show a border before the last row.
16
+ :ivar bool inner_heading_row_border: Show a border after the first row.
17
+ :ivar bool inner_row_border: Show a border in between every row.
18
+ :ivar bool outer_border: Show the top, left, right, and bottom border.
19
+ :ivar dict justify_columns: Horizontal justification. Keys are column indexes (int). Values are right/left/center.
20
+ :ivar int padding_left: Number of spaces to pad on the left side of every cell.
21
+ :ivar int padding_right: Number of spaces to pad on the right side of every cell.
22
+ """
23
+
24
+ CHAR_F_INNER_HORIZONTAL = "-"
25
+ CHAR_F_INNER_INTERSECT = "+"
26
+ CHAR_F_INNER_VERTICAL = "|"
27
+ CHAR_F_OUTER_LEFT_INTERSECT = "+"
28
+ CHAR_F_OUTER_LEFT_VERTICAL = "|"
29
+ CHAR_F_OUTER_RIGHT_INTERSECT = "+"
30
+ CHAR_F_OUTER_RIGHT_VERTICAL = "|"
31
+ CHAR_H_INNER_HORIZONTAL = "-"
32
+ CHAR_H_INNER_INTERSECT = "+"
33
+ CHAR_H_INNER_VERTICAL = "|"
34
+ CHAR_H_OUTER_LEFT_INTERSECT = "+"
35
+ CHAR_H_OUTER_LEFT_VERTICAL = "|"
36
+ CHAR_H_OUTER_RIGHT_INTERSECT = "+"
37
+ CHAR_H_OUTER_RIGHT_VERTICAL = "|"
38
+ CHAR_INNER_HORIZONTAL = "-"
39
+ CHAR_INNER_INTERSECT = "+"
40
+ CHAR_INNER_VERTICAL = "|"
41
+ CHAR_OUTER_BOTTOM_HORIZONTAL = "-"
42
+ CHAR_OUTER_BOTTOM_INTERSECT = "+"
43
+ CHAR_OUTER_BOTTOM_LEFT = "+"
44
+ CHAR_OUTER_BOTTOM_RIGHT = "+"
45
+ CHAR_OUTER_LEFT_INTERSECT = "+"
46
+ CHAR_OUTER_LEFT_VERTICAL = "|"
47
+ CHAR_OUTER_RIGHT_INTERSECT = "+"
48
+ CHAR_OUTER_RIGHT_VERTICAL = "|"
49
+ CHAR_OUTER_TOP_HORIZONTAL = "-"
50
+ CHAR_OUTER_TOP_INTERSECT = "+"
51
+ CHAR_OUTER_TOP_LEFT = "+"
52
+ CHAR_OUTER_TOP_RIGHT = "+"
53
+
54
+ def __init__(
55
+ self, table_data: Sequence[Sequence[str]], title: Optional[str] = None
56
+ ):
57
+ """Constructor.
58
+
59
+ :param iter table_data: List (empty or list of lists of strings) representing the table.
60
+ :param title: Optional title to show within the top border of the table.
61
+ """
62
+ self.table_data = table_data
63
+ self.title = title
64
+
65
+ self.inner_column_border = True
66
+ self.inner_footing_row_border = False
67
+ self.inner_heading_row_border = True
68
+ self.inner_row_border = False
69
+ self.outer_border = True
70
+
71
+ self.justify_columns = {} # {0: 'right', 1: 'left', 2: 'center'}
72
+ self.padding_left = 1
73
+ self.padding_right = 1
74
+
75
+ def horizontal_border(
76
+ self, style: str, outer_widths: Sequence[int]
77
+ ) -> Tuple[str, ...]:
78
+ """Build any kind of horizontal border for the table.
79
+
80
+ :param str style: Type of border to return.
81
+ :param iter outer_widths: List of widths (with padding) for each column.
82
+
83
+ :return: Prepared border as a tuple of strings.
84
+ :rtype: tuple
85
+ """
86
+ if style == "top":
87
+ horizontal = self.CHAR_OUTER_TOP_HORIZONTAL
88
+ left = self.CHAR_OUTER_TOP_LEFT
89
+ intersect = (
90
+ self.CHAR_OUTER_TOP_INTERSECT if self.inner_column_border else ""
91
+ )
92
+ right = self.CHAR_OUTER_TOP_RIGHT
93
+ title = self.title
94
+ elif style == "bottom":
95
+ horizontal = self.CHAR_OUTER_BOTTOM_HORIZONTAL
96
+ left = self.CHAR_OUTER_BOTTOM_LEFT
97
+ intersect = (
98
+ self.CHAR_OUTER_BOTTOM_INTERSECT if self.inner_column_border else ""
99
+ )
100
+ right = self.CHAR_OUTER_BOTTOM_RIGHT
101
+ title = None
102
+ elif style == "heading":
103
+ horizontal = self.CHAR_H_INNER_HORIZONTAL
104
+ left = self.CHAR_H_OUTER_LEFT_INTERSECT if self.outer_border else ""
105
+ intersect = self.CHAR_H_INNER_INTERSECT if self.inner_column_border else ""
106
+ right = self.CHAR_H_OUTER_RIGHT_INTERSECT if self.outer_border else ""
107
+ title = None
108
+ elif style == "footing":
109
+ horizontal = self.CHAR_F_INNER_HORIZONTAL
110
+ left = self.CHAR_F_OUTER_LEFT_INTERSECT if self.outer_border else ""
111
+ intersect = self.CHAR_F_INNER_INTERSECT if self.inner_column_border else ""
112
+ right = self.CHAR_F_OUTER_RIGHT_INTERSECT if self.outer_border else ""
113
+ title = None
114
+ else:
115
+ horizontal = self.CHAR_INNER_HORIZONTAL
116
+ left = self.CHAR_OUTER_LEFT_INTERSECT if self.outer_border else ""
117
+ intersect = self.CHAR_INNER_INTERSECT if self.inner_column_border else ""
118
+ right = self.CHAR_OUTER_RIGHT_INTERSECT if self.outer_border else ""
119
+ title = None
120
+ return build_border(outer_widths, horizontal, left, intersect, right, title)
121
+
122
+ def gen_row_lines(
123
+ self, row: Sequence[str], style: str, inner_widths: Sequence[int], height: int
124
+ ) -> Generator[Tuple[str, ...], None, None]:
125
+ r"""Combine cells in row and group them into lines with vertical borders.
126
+
127
+ Caller is expected to pass yielded lines to ''.join() to combine them into a printable line. Caller must append
128
+ newline character to the end of joined line.
129
+
130
+ In:
131
+ ['Row One Column One', 'Two', 'Three']
132
+ Out:
133
+ [
134
+ ('|', ' Row One Column One ', '|', ' Two ', '|', ' Three ', '|'),
135
+ ]
136
+
137
+ In:
138
+ ['Row One\nColumn One', 'Two', 'Three'],
139
+ Out:
140
+ [
141
+ ('|', ' Row One ', '|', ' Two ', '|', ' Three ', '|'),
142
+ ('|', ' Column One ', '|', ' ', '|', ' ', '|'),
143
+ ]
144
+
145
+ :param iter row: One row in the table. List of cells.
146
+ :param str style: Type of border characters to use.
147
+ :param iter inner_widths: List of widths (no padding) for each column.
148
+ :param int height: Inner height (no padding) (number of lines) to expand row to.
149
+
150
+ :return: Yields lines split into components in a list. Caller must ''.join() line.
151
+ """
152
+ cells_in_row = []
153
+
154
+ # Resize row if it doesn't have enough cells.
155
+ if len(row) != len(inner_widths):
156
+ row = row + [""] * (len(inner_widths) - len(row))
157
+
158
+ # Pad and align each cell. Split each cell into lines to support multi-line cells.
159
+ for i, cell in enumerate(row):
160
+ align = (self.justify_columns.get(i),)
161
+ inner_dimensions = (inner_widths[i], height)
162
+ padding = (self.padding_left, self.padding_right, 0, 0)
163
+ cells_in_row.append(
164
+ align_and_pad_cell(cell, align, inner_dimensions, padding)
165
+ )
166
+
167
+ # Determine border characters.
168
+ if style == "heading":
169
+ left = self.CHAR_H_OUTER_LEFT_VERTICAL if self.outer_border else ""
170
+ center = self.CHAR_H_INNER_VERTICAL if self.inner_column_border else ""
171
+ right = self.CHAR_H_OUTER_RIGHT_VERTICAL if self.outer_border else ""
172
+ elif style == "footing":
173
+ left = self.CHAR_F_OUTER_LEFT_VERTICAL if self.outer_border else ""
174
+ center = self.CHAR_F_INNER_VERTICAL if self.inner_column_border else ""
175
+ right = self.CHAR_F_OUTER_RIGHT_VERTICAL if self.outer_border else ""
176
+ else:
177
+ left = self.CHAR_OUTER_LEFT_VERTICAL if self.outer_border else ""
178
+ center = self.CHAR_INNER_VERTICAL if self.inner_column_border else ""
179
+ right = self.CHAR_OUTER_RIGHT_VERTICAL if self.outer_border else ""
180
+
181
+ # Yield each line.
182
+ yield from build_row(cells_in_row, left, center, right)
183
+
184
+ def gen_table(
185
+ self,
186
+ inner_widths: Sequence[int],
187
+ inner_heights: Sequence[int],
188
+ outer_widths: Sequence[int],
189
+ ) -> Generator[Tuple[str, ...], None, None]:
190
+ """Combine everything and yield every line of the entire table with borders.
191
+
192
+ :param iter inner_widths: List of widths (no padding) for each column.
193
+ :param iter inner_heights: List of heights (no padding) for each row.
194
+ :param iter outer_widths: List of widths (with padding) for each column.
195
+ :return:
196
+ """
197
+ # Yield top border.
198
+ if self.outer_border:
199
+ yield self.horizontal_border("top", outer_widths)
200
+
201
+ # Yield table body.
202
+ row_count = len(self.table_data)
203
+ last_row_index, before_last_row_index = row_count - 1, row_count - 2
204
+ for i, row in enumerate(self.table_data):
205
+ # Yield the row line by line (e.g. multi-line rows).
206
+ if self.inner_heading_row_border and i == 0:
207
+ style = "heading"
208
+ elif self.inner_footing_row_border and i == last_row_index:
209
+ style = "footing"
210
+ else:
211
+ style = "row"
212
+ yield from self.gen_row_lines(row, style, inner_widths, inner_heights[i])
213
+ # If this is the last row then break. No separator needed.
214
+ if i == last_row_index:
215
+ break
216
+ # Yield heading separator.
217
+ if self.inner_heading_row_border and i == 0:
218
+ yield self.horizontal_border("heading", outer_widths)
219
+ # Yield footing separator.
220
+ elif self.inner_footing_row_border and i == before_last_row_index:
221
+ yield self.horizontal_border("footing", outer_widths)
222
+ # Yield row separator.
223
+ elif self.inner_row_border:
224
+ yield self.horizontal_border("row", outer_widths)
225
+
226
+ # Yield bottom border.
227
+ if self.outer_border:
228
+ yield self.horizontal_border("bottom", outer_widths)
229
+
230
+ @property
231
+ def table(self) -> str:
232
+ """Return a large string of the entire table ready to be printed to the terminal."""
233
+ dimensions = max_dimensions(
234
+ self.table_data, self.padding_left, self.padding_right
235
+ )[:3]
236
+ return flatten(self.gen_table(*dimensions))
@@ -0,0 +1,174 @@
1
+ """Combine cells into rows."""
2
+
3
+ from typing import Generator, Iterator, Optional, Sequence, Union
4
+
5
+ from terminaltables3.width_and_alignment import visible_width
6
+
7
+
8
+ def combine(
9
+ line: Union[
10
+ Generator[Union[int, str], None, None], Iterator[Optional[Union[int, str]]]
11
+ ],
12
+ left: str,
13
+ intersect: Optional[str],
14
+ right: str,
15
+ ) -> Generator[int, None, None]:
16
+ """Zip borders between items in `line`.
17
+
18
+ e.g. ('l', '1', 'c', '2', 'c', '3', 'r')
19
+
20
+ :param iter line: List to iterate.
21
+ :param left: Left border.
22
+ :param intersect: Column separator.
23
+ :param right: Right border.
24
+
25
+ :return: Yields combined objects.
26
+ """
27
+ # Yield left border.
28
+ if left:
29
+ yield left
30
+
31
+ # Yield items with intersect characters.
32
+ if intersect:
33
+ try:
34
+ for j, i in enumerate(line, start=-len(line) + 1):
35
+ yield i
36
+ if j:
37
+ yield intersect
38
+ except TypeError: # Generator.
39
+ try:
40
+ item = next(line)
41
+ except StopIteration: # Was empty all along.
42
+ pass
43
+ else:
44
+ while True:
45
+ yield item
46
+ try:
47
+ peek = next(line)
48
+ except StopIteration:
49
+ break
50
+ yield intersect
51
+ item = peek
52
+ else:
53
+ yield from line
54
+
55
+ # Yield right border.
56
+ if right:
57
+ yield right
58
+
59
+
60
+ def build_border(
61
+ outer_widths: Sequence[int],
62
+ horizontal: str,
63
+ left: str,
64
+ intersect: str,
65
+ right: str,
66
+ title: Optional[str] = None,
67
+ ):
68
+ """Build the top/bottom/middle row. Optionally embed the table title within the border.
69
+
70
+ Title is hidden if it doesn't fit between the left/right characters/edges.
71
+
72
+ Example return value:
73
+ ('<', '-----', '+', '------', '+', '-------', '>')
74
+ ('<', 'My Table', '----', '+', '------->')
75
+
76
+ :param iter outer_widths: List of widths (with padding) for each column.
77
+ :param str horizontal: Character to stretch across each column.
78
+ :param str left: Left border.
79
+ :param str intersect: Column separator.
80
+ :param str right: Right border.
81
+ :param title: Overlay the title on the border between the left and right characters.
82
+
83
+ :return: Returns a generator of strings representing a border.
84
+ :rtype: iter
85
+ """
86
+ length = 0
87
+
88
+ # Hide title if it doesn't fit.
89
+ if title is not None and outer_widths:
90
+ try:
91
+ length = visible_width(title)
92
+ except TypeError:
93
+ title = str(title)
94
+ length = visible_width(title)
95
+ if length > sum(outer_widths) + len(intersect) * (len(outer_widths) - 1):
96
+ title = None
97
+
98
+ # Handle no title.
99
+ if title is None or not outer_widths or not horizontal:
100
+ return combine((horizontal * c for c in outer_widths), left, intersect, right)
101
+
102
+ # Handle title fitting in the first column.
103
+ if length == outer_widths[0]:
104
+ return combine(
105
+ [title] + [horizontal * c for c in outer_widths[1:]], left, intersect, right
106
+ )
107
+ if length < outer_widths[0]:
108
+ columns = [title + horizontal * (outer_widths[0] - length)] + [
109
+ horizontal * c for c in outer_widths[1:]
110
+ ]
111
+ return combine(columns, left, intersect, right)
112
+
113
+ # Handle wide titles/narrow columns.
114
+ columns_and_intersects = [title]
115
+ for width in combine(outer_widths, None, bool(intersect), None):
116
+ # If title is taken care of.
117
+ if length < 1:
118
+ columns_and_intersects.append(
119
+ intersect if width is True else horizontal * width
120
+ )
121
+ # If title's last character overrides an intersect character.
122
+ elif width is True and length == 1:
123
+ length = 0
124
+ # If this is an intersect character that is overridden by the title.
125
+ elif width is True:
126
+ length -= 1
127
+ # If title's last character is within a column.
128
+ elif width >= length:
129
+ columns_and_intersects[0] += horizontal * (
130
+ width - length
131
+ ) # Append horizontal chars to title.
132
+ length = 0
133
+ # If remainder of title won't fit in a column.
134
+ else:
135
+ length -= width
136
+
137
+ return combine(columns_and_intersects, left, None, right)
138
+
139
+
140
+ def build_row(row, left, center, right):
141
+ """Combine single or multi-lined cells into a single row of list of lists including borders.
142
+
143
+ Row must already be padded and extended so each cell has the same number of lines.
144
+
145
+ Example return value:
146
+ [
147
+ ['>', 'Left ', '|', 'Center', '|', 'Right', '<'],
148
+ ['>', 'Cell1', '|', 'Cell2 ', '|', 'Cell3', '<'],
149
+ ]
150
+
151
+ :param iter row: List of cells for one row.
152
+ :param str left: Left border.
153
+ :param str center: Column separator.
154
+ :param str right: Right border.
155
+
156
+ :return: Yields other generators that yield strings.
157
+ :rtype: iter
158
+ """
159
+ if not row or not row[0]:
160
+ yield combine((), left, center, right)
161
+ return
162
+ for row_index in range(len(row[0])):
163
+ yield combine((c[row_index] for c in row), left, center, right)
164
+
165
+
166
+ def flatten(table):
167
+ """Flatten table data into a single string with newlines.
168
+
169
+ :param iter table: Padded and bordered table data.
170
+
171
+ :return: Joined rows/cells.
172
+ :rtype: str
173
+ """
174
+ return "\n".join("".join(r) for r in table)
@@ -0,0 +1,73 @@
1
+ """GithubFlavoredMarkdownTable class."""
2
+
3
+ from typing import Sequence
4
+
5
+ from terminaltables3.ascii_table import AsciiTable
6
+ from terminaltables3.build import combine
7
+
8
+
9
+ class GithubFlavoredMarkdownTable(AsciiTable):
10
+ """Github flavored markdown table.
11
+
12
+ https://help.github.com/articles/github-flavored-markdown/#tables
13
+
14
+ :ivar iter table_data: List (empty or list of lists of strings) representing the table.
15
+ :ivar dict justify_columns: Horizontal justification. Keys are column indexes (int). Values are right/left/center.
16
+ """
17
+
18
+ def __init__(self, table_data: Sequence[Sequence[str]]):
19
+ """Constructor.
20
+
21
+ :param iter table_data: List (empty or list of lists of strings) representing the table.
22
+ """
23
+ # Github flavored markdown table won't support title.
24
+ super().__init__(table_data)
25
+
26
+ def horizontal_border(self, _, outer_widths):
27
+ """Handle the GitHub heading border.
28
+
29
+ E.g.:
30
+ |:---|:---:|---:|----|
31
+
32
+ :param _: Unused.
33
+ :param iter outer_widths: List of widths (with padding) for each column.
34
+
35
+ :return: Prepared border strings in a generator.
36
+ :rtype: iter
37
+ """
38
+ horizontal = str(self.CHAR_INNER_HORIZONTAL)
39
+ left = self.CHAR_OUTER_LEFT_VERTICAL
40
+ intersect = self.CHAR_INNER_VERTICAL
41
+ right = self.CHAR_OUTER_RIGHT_VERTICAL
42
+
43
+ columns = []
44
+ for i, width in enumerate(outer_widths):
45
+ justify = self.justify_columns.get(i)
46
+ width = max(
47
+ 3, width
48
+ ) # Width should be at least 3 so justification can be applied.
49
+ if justify == "left":
50
+ columns.append(":" + horizontal * (width - 1))
51
+ elif justify == "right":
52
+ columns.append(horizontal * (width - 1) + ":")
53
+ elif justify == "center":
54
+ columns.append(":" + horizontal * (width - 2) + ":")
55
+ else:
56
+ columns.append(horizontal * width)
57
+
58
+ return combine(columns, left, intersect, right)
59
+
60
+ def gen_table(self, inner_widths, inner_heights, outer_widths):
61
+ """Combine everything and yield every line of the entire table with borders.
62
+
63
+ :param iter inner_widths: List of widths (no padding) for each column.
64
+ :param iter inner_heights: List of heights (no padding) for each row.
65
+ :param iter outer_widths: List of widths (with padding) for each column.
66
+ :return:
67
+ """
68
+ for i, row in enumerate(self.table_data):
69
+ # Yield the row line by line (e.g. multi-line rows).
70
+ yield from self.gen_row_lines(row, "row", inner_widths, inner_heights[i])
71
+ # Yield heading separator.
72
+ if i == 0:
73
+ yield self.horizontal_border(None, outer_widths)
@@ -0,0 +1,173 @@
1
+ """Additional simple tables defined here."""
2
+
3
+ from terminaltables3.ascii_table import AsciiTable
4
+ from terminaltables3.terminal_io import IS_WINDOWS
5
+
6
+
7
+ class UnixTable(AsciiTable):
8
+ """Draw a table using box-drawing characters on Unix platforms. Table borders won't have any gaps between lines.
9
+
10
+ Similar to the tables shown on PC BIOS boot messages, but not double-lined.
11
+ """
12
+
13
+ CHAR_F_INNER_HORIZONTAL = "\033(0\x71\033(B"
14
+ CHAR_F_INNER_INTERSECT = "\033(0\x6e\033(B"
15
+ CHAR_F_INNER_VERTICAL = "\033(0\x78\033(B"
16
+ CHAR_F_OUTER_LEFT_INTERSECT = "\033(0\x74\033(B"
17
+ CHAR_F_OUTER_LEFT_VERTICAL = "\033(0\x78\033(B"
18
+ CHAR_F_OUTER_RIGHT_INTERSECT = "\033(0\x75\033(B"
19
+ CHAR_F_OUTER_RIGHT_VERTICAL = "\033(0\x78\033(B"
20
+ CHAR_H_INNER_HORIZONTAL = "\033(0\x71\033(B"
21
+ CHAR_H_INNER_INTERSECT = "\033(0\x6e\033(B"
22
+ CHAR_H_INNER_VERTICAL = "\033(0\x78\033(B"
23
+ CHAR_H_OUTER_LEFT_INTERSECT = "\033(0\x74\033(B"
24
+ CHAR_H_OUTER_LEFT_VERTICAL = "\033(0\x78\033(B"
25
+ CHAR_H_OUTER_RIGHT_INTERSECT = "\033(0\x75\033(B"
26
+ CHAR_H_OUTER_RIGHT_VERTICAL = "\033(0\x78\033(B"
27
+ CHAR_INNER_HORIZONTAL = "\033(0\x71\033(B"
28
+ CHAR_INNER_INTERSECT = "\033(0\x6e\033(B"
29
+ CHAR_INNER_VERTICAL = "\033(0\x78\033(B"
30
+ CHAR_OUTER_BOTTOM_HORIZONTAL = "\033(0\x71\033(B"
31
+ CHAR_OUTER_BOTTOM_INTERSECT = "\033(0\x76\033(B"
32
+ CHAR_OUTER_BOTTOM_LEFT = "\033(0\x6d\033(B"
33
+ CHAR_OUTER_BOTTOM_RIGHT = "\033(0\x6a\033(B"
34
+ CHAR_OUTER_LEFT_INTERSECT = "\033(0\x74\033(B"
35
+ CHAR_OUTER_LEFT_VERTICAL = "\033(0\x78\033(B"
36
+ CHAR_OUTER_RIGHT_INTERSECT = "\033(0\x75\033(B"
37
+ CHAR_OUTER_RIGHT_VERTICAL = "\033(0\x78\033(B"
38
+ CHAR_OUTER_TOP_HORIZONTAL = "\033(0\x71\033(B"
39
+ CHAR_OUTER_TOP_INTERSECT = "\033(0\x77\033(B"
40
+ CHAR_OUTER_TOP_LEFT = "\033(0\x6c\033(B"
41
+ CHAR_OUTER_TOP_RIGHT = "\033(0\x6b\033(B"
42
+
43
+ @property
44
+ def table(self):
45
+ """Return a large string of the entire table ready to be printed to the terminal."""
46
+ ascii_table = super().table
47
+ optimized = ascii_table.replace("\033(B\033(0", "")
48
+ return optimized
49
+
50
+
51
+ class WindowsTable(AsciiTable):
52
+ """Draw a table using box-drawing characters on Windows platforms. This uses Code Page 437. Single-line borders.
53
+
54
+ From: http://en.wikipedia.org/wiki/Code_page_437#Characters
55
+ """
56
+
57
+ CHAR_F_INNER_HORIZONTAL = b"\xc4".decode("ibm437")
58
+ CHAR_F_INNER_INTERSECT = b"\xc5".decode("ibm437")
59
+ CHAR_F_INNER_VERTICAL = b"\xb3".decode("ibm437")
60
+ CHAR_F_OUTER_LEFT_INTERSECT = b"\xc3".decode("ibm437")
61
+ CHAR_F_OUTER_LEFT_VERTICAL = b"\xb3".decode("ibm437")
62
+ CHAR_F_OUTER_RIGHT_INTERSECT = b"\xb4".decode("ibm437")
63
+ CHAR_F_OUTER_RIGHT_VERTICAL = b"\xb3".decode("ibm437")
64
+ CHAR_H_INNER_HORIZONTAL = b"\xc4".decode("ibm437")
65
+ CHAR_H_INNER_INTERSECT = b"\xc5".decode("ibm437")
66
+ CHAR_H_INNER_VERTICAL = b"\xb3".decode("ibm437")
67
+ CHAR_H_OUTER_LEFT_INTERSECT = b"\xc3".decode("ibm437")
68
+ CHAR_H_OUTER_LEFT_VERTICAL = b"\xb3".decode("ibm437")
69
+ CHAR_H_OUTER_RIGHT_INTERSECT = b"\xb4".decode("ibm437")
70
+ CHAR_H_OUTER_RIGHT_VERTICAL = b"\xb3".decode("ibm437")
71
+ CHAR_INNER_HORIZONTAL = b"\xc4".decode("ibm437")
72
+ CHAR_INNER_INTERSECT = b"\xc5".decode("ibm437")
73
+ CHAR_INNER_VERTICAL = b"\xb3".decode("ibm437")
74
+ CHAR_OUTER_BOTTOM_HORIZONTAL = b"\xc4".decode("ibm437")
75
+ CHAR_OUTER_BOTTOM_INTERSECT = b"\xc1".decode("ibm437")
76
+ CHAR_OUTER_BOTTOM_LEFT = b"\xc0".decode("ibm437")
77
+ CHAR_OUTER_BOTTOM_RIGHT = b"\xd9".decode("ibm437")
78
+ CHAR_OUTER_LEFT_INTERSECT = b"\xc3".decode("ibm437")
79
+ CHAR_OUTER_LEFT_VERTICAL = b"\xb3".decode("ibm437")
80
+ CHAR_OUTER_RIGHT_INTERSECT = b"\xb4".decode("ibm437")
81
+ CHAR_OUTER_RIGHT_VERTICAL = b"\xb3".decode("ibm437")
82
+ CHAR_OUTER_TOP_HORIZONTAL = b"\xc4".decode("ibm437")
83
+ CHAR_OUTER_TOP_INTERSECT = b"\xc2".decode("ibm437")
84
+ CHAR_OUTER_TOP_LEFT = b"\xda".decode("ibm437")
85
+ CHAR_OUTER_TOP_RIGHT = b"\xbf".decode("ibm437")
86
+
87
+
88
+ class WindowsTableDouble(AsciiTable):
89
+ """Draw a table using box-drawing characters on Windows platforms. This uses Code Page 437. Double-line borders."""
90
+
91
+ CHAR_F_INNER_HORIZONTAL = b"\xcd".decode("ibm437")
92
+ CHAR_F_INNER_INTERSECT = b"\xce".decode("ibm437")
93
+ CHAR_F_INNER_VERTICAL = b"\xba".decode("ibm437")
94
+ CHAR_F_OUTER_LEFT_INTERSECT = b"\xcc".decode("ibm437")
95
+ CHAR_F_OUTER_LEFT_VERTICAL = b"\xba".decode("ibm437")
96
+ CHAR_F_OUTER_RIGHT_INTERSECT = b"\xb9".decode("ibm437")
97
+ CHAR_F_OUTER_RIGHT_VERTICAL = b"\xba".decode("ibm437")
98
+ CHAR_H_INNER_HORIZONTAL = b"\xcd".decode("ibm437")
99
+ CHAR_H_INNER_INTERSECT = b"\xce".decode("ibm437")
100
+ CHAR_H_INNER_VERTICAL = b"\xba".decode("ibm437")
101
+ CHAR_H_OUTER_LEFT_INTERSECT = b"\xcc".decode("ibm437")
102
+ CHAR_H_OUTER_LEFT_VERTICAL = b"\xba".decode("ibm437")
103
+ CHAR_H_OUTER_RIGHT_INTERSECT = b"\xb9".decode("ibm437")
104
+ CHAR_H_OUTER_RIGHT_VERTICAL = b"\xba".decode("ibm437")
105
+ CHAR_INNER_HORIZONTAL = b"\xcd".decode("ibm437")
106
+ CHAR_INNER_INTERSECT = b"\xce".decode("ibm437")
107
+ CHAR_INNER_VERTICAL = b"\xba".decode("ibm437")
108
+ CHAR_OUTER_BOTTOM_HORIZONTAL = b"\xcd".decode("ibm437")
109
+ CHAR_OUTER_BOTTOM_INTERSECT = b"\xca".decode("ibm437")
110
+ CHAR_OUTER_BOTTOM_LEFT = b"\xc8".decode("ibm437")
111
+ CHAR_OUTER_BOTTOM_RIGHT = b"\xbc".decode("ibm437")
112
+ CHAR_OUTER_LEFT_INTERSECT = b"\xcc".decode("ibm437")
113
+ CHAR_OUTER_LEFT_VERTICAL = b"\xba".decode("ibm437")
114
+ CHAR_OUTER_RIGHT_INTERSECT = b"\xb9".decode("ibm437")
115
+ CHAR_OUTER_RIGHT_VERTICAL = b"\xba".decode("ibm437")
116
+ CHAR_OUTER_TOP_HORIZONTAL = b"\xcd".decode("ibm437")
117
+ CHAR_OUTER_TOP_INTERSECT = b"\xcb".decode("ibm437")
118
+ CHAR_OUTER_TOP_LEFT = b"\xc9".decode("ibm437")
119
+ CHAR_OUTER_TOP_RIGHT = b"\xbb".decode("ibm437")
120
+
121
+
122
+ class SingleTable(WindowsTable if IS_WINDOWS else UnixTable):
123
+ """Cross-platform table with single-line box-drawing characters.
124
+
125
+ :ivar iter table_data: List (empty or list of lists of strings) representing the table.
126
+ :ivar str title: Optional title to show within the top border of the table.
127
+ :ivar bool inner_column_border: Separates columns.
128
+ :ivar bool inner_footing_row_border: Show a border before the last row.
129
+ :ivar bool inner_heading_row_border: Show a border after the first row.
130
+ :ivar bool inner_row_border: Show a border in between every row.
131
+ :ivar bool outer_border: Show the top, left, right, and bottom border.
132
+ :ivar dict justify_columns: Horizontal justification. Keys are column indexes (int). Values are right/left/center.
133
+ :ivar int padding_left: Number of spaces to pad on the left side of every cell.
134
+ :ivar int padding_right: Number of spaces to pad on the right side of every cell.
135
+ """
136
+
137
+
138
+ class DoubleTable(WindowsTableDouble):
139
+ """Cross-platform table with box-drawing characters. On Windows it's double borders, on Linux/OSX it's unicode.
140
+
141
+ :ivar iter table_data: List (empty or list of lists of strings) representing the table.
142
+ :ivar str title: Optional title to show within the top border of the table.
143
+ :ivar bool inner_column_border: Separates columns.
144
+ :ivar bool inner_footing_row_border: Show a border before the last row.
145
+ :ivar bool inner_heading_row_border: Show a border after the first row.
146
+ :ivar bool inner_row_border: Show a border in between every row.
147
+ :ivar bool outer_border: Show the top, left, right, and bottom border.
148
+ :ivar dict justify_columns: Horizontal justification. Keys are column indexes (int). Values are right/left/center.
149
+ :ivar int padding_left: Number of spaces to pad on the left side of every cell.
150
+ :ivar int padding_right: Number of spaces to pad on the right side of every cell.
151
+ """
152
+
153
+
154
+ class PorcelainTable(AsciiTable):
155
+ """An AsciiTable stripped to a minimum.
156
+
157
+ Meant to be machine passable and roughly follow format set by git --porcelain option (hence the name).
158
+
159
+ :ivar iter table_data: List (empty or list of lists of strings) representing the table.
160
+ """
161
+
162
+ def __init__(self, table_data):
163
+ """Constructor.
164
+
165
+ :param iter table_data: List (empty or list of lists of strings) representing the table.
166
+ """
167
+ # Porcelain table won't support title since it has no outer birders.
168
+ super().__init__(table_data)
169
+
170
+ # Removes outer border, and inner footing and header row borders.
171
+ self.inner_footing_row_border = False
172
+ self.inner_heading_row_border = False
173
+ self.outer_border = False
@@ -0,0 +1,102 @@
1
+ """Get info about the current terminal window/screen buffer."""
2
+
3
+ import ctypes
4
+ import struct
5
+ import sys
6
+ from typing import Tuple, Union
7
+
8
+ DEFAULT_HEIGHT = 24
9
+ DEFAULT_WIDTH = 79
10
+ INVALID_HANDLE_VALUE = -1
11
+ IS_WINDOWS = sys.platform == "win32"
12
+ STD_ERROR_HANDLE = -12
13
+ STD_OUTPUT_HANDLE = -11
14
+
15
+
16
+ def get_console_info(kernel32, handle: int) -> Tuple[int, int]:
17
+ """Get information about this current console window (Windows only).
18
+
19
+ https://github.com/Robpol86/colorclass/blob/ab42da59/colorclass/windows.py#L111
20
+
21
+ :raise OSError: When handle is invalid or GetConsoleScreenBufferInfo API call fails.
22
+
23
+ :param ctypes.windll.kernel32 kernel32: Loaded kernel32 instance.
24
+ :param int handle: stderr or stdout handle.
25
+
26
+ :return: Width (number of characters) and height (number of lines) of the terminal.
27
+ :rtype: tuple
28
+ """
29
+ if handle == INVALID_HANDLE_VALUE:
30
+ raise OSError("Invalid handle.")
31
+
32
+ # Query Win32 API.
33
+ lpcsbi = ctypes.create_string_buffer(22) # Populated by GetConsoleScreenBufferInfo.
34
+ if not kernel32.GetConsoleScreenBufferInfo(handle, lpcsbi):
35
+ raise ctypes.WinError() # Subclass of OSError.
36
+
37
+ # Parse data.
38
+ left, top, right, bottom = struct.unpack("hhhhHhhhhhh", lpcsbi.raw)[5:-2]
39
+ width, height = right - left, bottom - top
40
+ return width, height
41
+
42
+
43
+ def terminal_size(kernel32=None) -> Tuple[int, int]:
44
+ """Get the width and height of the terminal.
45
+
46
+ http://code.activestate.com/recipes/440694-determine-size-of-console-window-on-windows/
47
+ http://stackoverflow.com/questions/17993814/why-the-irrelevant-code-made-a-difference
48
+
49
+ :param kernel32: Optional mock kernel32 object. For testing.
50
+
51
+ :return: Width (number of characters) and height (number of lines) of the terminal.
52
+ :rtype: tuple
53
+ """
54
+ if IS_WINDOWS:
55
+ kernel32 = kernel32 or ctypes.windll.kernel32
56
+ try:
57
+ return get_console_info(kernel32, kernel32.GetStdHandle(STD_ERROR_HANDLE))
58
+ except OSError:
59
+ try:
60
+ return get_console_info(
61
+ kernel32, kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
62
+ )
63
+ except OSError:
64
+ return DEFAULT_WIDTH, DEFAULT_HEIGHT
65
+
66
+ try:
67
+ device = __import__("fcntl").ioctl(
68
+ 0, __import__("termios").TIOCGWINSZ, "\0\0\0\0\0\0\0\0"
69
+ )
70
+ except OSError:
71
+ return DEFAULT_WIDTH, DEFAULT_HEIGHT
72
+ height, width = struct.unpack("hhhh", device)[:2]
73
+ return width, height
74
+
75
+
76
+ def set_terminal_title(title: Union[str, bytes], kernel32=None) -> bool:
77
+ """Set the terminal title.
78
+
79
+ :param title: The title to set (string, unicode, bytes accepted).
80
+ :param kernel32: Optional mock kernel32 object. For testing.
81
+
82
+ :return: If title changed successfully (Windows only, always True on Linux/OSX).
83
+ :rtype: bool
84
+ """
85
+ try:
86
+ title_bytes = title.encode("utf-8")
87
+ except AttributeError:
88
+ title_bytes = title
89
+
90
+ if IS_WINDOWS:
91
+ kernel32 = kernel32 or ctypes.windll.kernel32
92
+ try:
93
+ is_ascii = all(ord(c) < 128 for c in title) # str/unicode.
94
+ except TypeError:
95
+ is_ascii = all(c < 128 for c in title) # bytes.
96
+ if is_ascii:
97
+ return kernel32.SetConsoleTitleA(title_bytes) != 0
98
+ return kernel32.SetConsoleTitleW(title) != 0
99
+
100
+ # Linux/OSX.
101
+ sys.stdout.write(b"\033]0;" + title_bytes + b"\007")
102
+ return True
@@ -0,0 +1,196 @@
1
+ """Functions that handle alignment, padding, widths, etc."""
2
+
3
+ import re
4
+ import unicodedata
5
+ from typing import Sequence, Tuple
6
+
7
+ from terminaltables3.terminal_io import terminal_size
8
+
9
+ RE_COLOR_ANSI = re.compile(r"(\033\[[\d;]+m)")
10
+
11
+
12
+ def visible_width(string: str) -> int:
13
+ """Get the visible width of a unicode string.
14
+
15
+ Some CJK unicode characters are more than one byte unlike ASCII and latin unicode characters.
16
+
17
+ From: https://github.com/Robpol86/terminaltables3/pull/9
18
+
19
+ :param str string: String to measure.
20
+
21
+ :return: String's width.
22
+ :rtype: int
23
+ """
24
+ if "\033" in string:
25
+ string = RE_COLOR_ANSI.sub("", string)
26
+
27
+ # Convert to unicode.
28
+ try:
29
+ string = string.decode("u8")
30
+ except (AttributeError, UnicodeEncodeError):
31
+ pass
32
+
33
+ width = 0
34
+ for char in string:
35
+ if unicodedata.east_asian_width(char) in ("F", "W"):
36
+ width += 2
37
+ else:
38
+ width += 1
39
+
40
+ return width
41
+
42
+
43
+ def align_and_pad_cell(
44
+ string: str,
45
+ align: Tuple,
46
+ inner_dimensions: Tuple,
47
+ padding: Sequence[int],
48
+ space: str = " ",
49
+ ) -> list[str]:
50
+ """Align a string horizontally and vertically. Also add additional padding in both dimensions.
51
+
52
+ :param str string: Input string to operate on.
53
+ :param tuple align: Tuple that contains one of left/center/right and/or top/middle/bottom.
54
+ :param tuple inner_dimensions: Width and height ints to expand string to without padding.
55
+ :param iter padding: Number of space chars for left, right, top, and bottom (4 ints).
56
+ :param str space: Character to use as white space for resizing/padding (use single visible chars only).
57
+
58
+ :return: Padded cell split into lines.
59
+ :rtype: list
60
+ """
61
+ if not hasattr(string, "splitlines"):
62
+ string = str(string)
63
+
64
+ # Handle trailing newlines or empty strings, str.splitlines() does not satisfy.
65
+ lines = string.splitlines() or [""]
66
+ if string.endswith("\n"):
67
+ lines.append("")
68
+
69
+ # Vertically align and pad.
70
+ if "bottom" in align:
71
+ lines = (
72
+ ([""] * (inner_dimensions[1] - len(lines) + padding[2]))
73
+ + lines
74
+ + ([""] * padding[3])
75
+ )
76
+ elif "middle" in align:
77
+ delta = inner_dimensions[1] - len(lines)
78
+ lines = (
79
+ ([""] * (delta // 2 + delta % 2 + padding[2]))
80
+ + lines
81
+ + ([""] * (delta // 2 + padding[3]))
82
+ )
83
+ else:
84
+ lines = (
85
+ ([""] * padding[2])
86
+ + lines
87
+ + ([""] * (inner_dimensions[1] - len(lines) + padding[3]))
88
+ )
89
+
90
+ # Horizontally align and pad.
91
+ for i, line in enumerate(lines):
92
+ new_width = inner_dimensions[0] + len(line) - visible_width(line)
93
+ if "right" in align:
94
+ lines[i] = line.rjust(padding[0] + new_width, space) + (space * padding[1])
95
+ elif "center" in align:
96
+ lines[i] = (
97
+ (space * padding[0])
98
+ + line.center(new_width, space)
99
+ + (space * padding[1])
100
+ )
101
+ else:
102
+ lines[i] = (space * padding[0]) + line.ljust(new_width + padding[1], space)
103
+
104
+ return lines
105
+
106
+
107
+ def max_dimensions(
108
+ table_data, padding_left=0, padding_right=0, padding_top=0, padding_bottom=0
109
+ ):
110
+ """Get maximum widths of each column and maximum height of each row.
111
+
112
+ :param iter table_data: List of list of strings (unmodified table data).
113
+ :param int padding_left: Number of space chars on left side of cell.
114
+ :param int padding_right: Number of space chars on right side of cell.
115
+ :param int padding_top: Number of empty lines on top side of cell.
116
+ :param int padding_bottom: Number of empty lines on bottom side of cell.
117
+
118
+ :return: 4-item tuple of n-item lists. Inner column widths and row heights, outer column widths and row heights.
119
+ :rtype: tuple
120
+ """
121
+ inner_widths = [0] * (max(len(r) for r in table_data) if table_data else 0)
122
+ inner_heights = [0] * len(table_data)
123
+
124
+ # Find max width and heights.
125
+ for j, row in enumerate(table_data):
126
+ for i, cell in enumerate(row):
127
+ if not hasattr(cell, "count") or not hasattr(cell, "splitlines"):
128
+ cell = str(cell)
129
+ if not cell:
130
+ continue
131
+ inner_heights[j] = max(inner_heights[j], cell.count("\n") + 1)
132
+ inner_widths[i] = max(
133
+ inner_widths[i],
134
+ *[visible_width(the_line) for the_line in cell.splitlines()]
135
+ )
136
+
137
+ # Calculate with padding.
138
+ outer_widths = [padding_left + i + padding_right for i in inner_widths]
139
+ outer_heights = [padding_top + i + padding_bottom for i in inner_heights]
140
+
141
+ return inner_widths, inner_heights, outer_widths, outer_heights
142
+
143
+
144
+ def column_max_width(
145
+ inner_widths: Sequence[int],
146
+ column_number: int,
147
+ outer_border: int,
148
+ inner_border: int,
149
+ padding: int,
150
+ ) -> int:
151
+ """Determine the maximum width of a column based on the current terminal width.
152
+
153
+ :param iter inner_widths: List of widths (no padding) for each column.
154
+ :param int column_number: The column number to query.
155
+ :param int outer_border: Sum of left and right outer border visible widths.
156
+ :param int inner_border: Visible width of the inner border character.
157
+ :param int padding: Total padding per cell (left + right padding).
158
+
159
+ :return: The maximum width the column can be without causing line wrapping.
160
+ """
161
+ column_count = len(inner_widths)
162
+ terminal_width = terminal_size()[0]
163
+
164
+ # Count how much space padding, outer, and inner borders take up.
165
+ non_data_space = outer_border
166
+ non_data_space += inner_border * (column_count - 1)
167
+ non_data_space += column_count * padding
168
+
169
+ # Exclude selected column's width.
170
+ data_space = sum(inner_widths) - inner_widths[column_number]
171
+
172
+ return terminal_width - data_space - non_data_space
173
+
174
+
175
+ def table_width(
176
+ outer_widths: Sequence[int], outer_border: int, inner_border: int
177
+ ) -> int:
178
+ """Determine the width of the entire table including borders and padding.
179
+
180
+ :param iter outer_widths: List of widths (with padding) for each column.
181
+ :param int outer_border: Sum of left and right outer border visible widths.
182
+ :param int inner_border: Visible width of the inner border character.
183
+
184
+ :return: The width of the table.
185
+ :rtype: int
186
+ """
187
+ column_count = len(outer_widths)
188
+
189
+ # Count how much space outer and inner borders take up.
190
+ non_data_space = outer_border
191
+ if column_count:
192
+ non_data_space += inner_border * (column_count - 1)
193
+
194
+ # Space of all columns and their padding.
195
+ data_space = sum(outer_widths)
196
+ return data_space + non_data_space
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 Robpol86
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,103 @@
1
+ Metadata-Version: 2.1
2
+ Name: terminaltables3
3
+ Version: 4.0.0
4
+ Summary: Generate simple tables in terminals from a nested list of strings. Fork of terminaltables.
5
+ Home-page: https://github.com/matthewdeanmartin/terminaltables3
6
+ License: MIT
7
+ Keywords: Shell,Bash,ANSI,ASCII,terminal,tables
8
+ Author: Robpol86
9
+ Author-email: robpol86@gmail.com
10
+ Requires-Python: >=3.8
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Environment :: Console
13
+ Classifier: Environment :: MacOS X
14
+ Classifier: Environment :: Win32 (MS Windows)
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: MacOS :: MacOS X
18
+ Classifier: Operating System :: Microsoft :: Windows
19
+ Classifier: Operating System :: POSIX
20
+ Classifier: Operating System :: POSIX :: Linux
21
+ Classifier: Programming Language :: Python :: 3
22
+ Classifier: Programming Language :: Python :: 3.8
23
+ Classifier: Programming Language :: Python :: 3.9
24
+ Classifier: Programming Language :: Python :: 3.10
25
+ Classifier: Programming Language :: Python :: 3.11
26
+ Classifier: Programming Language :: Python :: 3.12
27
+ Classifier: Programming Language :: Python :: 3.13
28
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
29
+ Classifier: Topic :: Software Development :: Libraries
30
+ Classifier: Topic :: Terminals
31
+ Classifier: Topic :: Text Processing :: Markup
32
+ Project-URL: Bug Tracker, https://github.com/matthewdeanmartin/terminaltables3/issues
33
+ Project-URL: Change Log, https://github.com/matthewdeanmartin/terminaltables3/blob/master/CHANGELOG.md
34
+ Project-URL: Documentation, https://github.com/matthewdeanmartin/terminaltables3
35
+ Project-URL: Repository, https://github.com/matthewdeanmartin/terminaltables3
36
+ Description-Content-Type: text/markdown
37
+
38
+ # What is it
39
+
40
+ Easily draw tables in terminal/console applications from a list of lists of strings. Supports multi-line rows.
41
+
42
+ Tested on Python 3.8+
43
+
44
+ **This is a fork of the terminaltables project. Which is archived and unmaintained. This library is in a new namespace
45
+ but should otherwise be a drop in replacement. Maintaining goals consist of maintaining ecosystem compatibility, type
46
+ annotations and responding to community pull requests.**
47
+
48
+ To Upgrade
49
+ ==========
50
+ Replace all instances of `terminaltables` with `terminaltables3` in your code. If other libraries depend on `terminaltables`
51
+ in your venv they will not conflict because it is a new namespace.
52
+
53
+ As of right now, the documentation as the robpol86 version.
54
+
55
+ 📖 Full documentation: https://robpol86.github.io/terminaltables
56
+
57
+ Quickstart
58
+ ==========
59
+
60
+ Install:
61
+
62
+ ```bash
63
+ pip install terminaltables3
64
+ ```
65
+
66
+ Usage:
67
+
68
+ ```python
69
+ from terminaltables3 import AsciiTable
70
+
71
+ table_data = [
72
+ ["Heading1", "Heading2"],
73
+ ["row1 column1", "row1 column2"],
74
+ ["row2 column1", "row2 column2"],
75
+ ["row3 column1", "row3 column2"],
76
+ ]
77
+ table = AsciiTable(table_data)
78
+ print
79
+ table.table
80
+ ```
81
+
82
+ ```bash
83
+ +--------------+--------------+
84
+ | Heading1 | Heading2 |
85
+ +--------------+--------------+
86
+ | row1 column1 | row1 column2 |
87
+ | row2 column1 | row2 column2 |
88
+ | row3 column1 | row3 column2 |
89
+ +--------------+--------------+
90
+ ```
91
+
92
+ Example Implementations
93
+ =======================
94
+ ![Example Scripts Screenshot](https://github.com/matthewdeanmartin/terminaltables/blob/master/docs/examples.png?raw=true)
95
+
96
+ Source code for examples:
97
+
98
+ - [example1.py](https://github.com/matthewdeanmartin/terminaltables/blob/master/example1.py)
99
+ - [example2.py](https://github.com/matthewdeanmartin/terminaltables/blob/master/example2.py)
100
+ - [example3.py](https://github.com/matthewdeanmartin/terminaltables/blob/master/example3.py)
101
+
102
+ [Change Log](https://github.com/matthewdeanmartin/terminaltables/blob/master/CHANGELOG.md)
103
+
@@ -0,0 +1,13 @@
1
+ terminaltables3/__init__.py,sha256=QfDCH6_ErpnLwkRzmn3Keb7nsHSUwAPkeMZ5lkqQVI0,637
2
+ terminaltables3/ascii_table.py,sha256=oM-eKgq5e9Mbh-KGnZ-D7G0E6a98x3c_lJwPyvpqm3g,2834
3
+ terminaltables3/base_table.py,sha256=AxC8UaBJVZZaq20Ce7dOkVCWo4iS3SxktmVR2S5DrzY,10181
4
+ terminaltables3/build.py,sha256=8G3dYv1ISBTZTgKF4PzlyD8Q4QEytXk6j5HCaysSO1A,5431
5
+ terminaltables3/github_table.py,sha256=z8kUWxvtaOUvLzudvyY4zZqBkx4B-lKbj3dYmLdq7zM,2797
6
+ terminaltables3/other_tables.py,sha256=hvbQ1bPD67-L_h_t_UR1BJhUtr4WqZ6KujEt7xqlINs,8427
7
+ terminaltables3/terminal_io.py,sha256=cBpaia54zVhMW4uRHhA5YkrEFYgAsHmBkaOSzMmPccA,3420
8
+ terminaltables3/width_and_alignment.py,sha256=szNw8XzOvZ2WIca1bnF9qI1a7R6lRNUf86Pxtiwynhk,6679
9
+ terminaltables3-4.0.0.dist-info/LICENSE,sha256=LhMg_TN9sGoxNtD0EBvWLxMknJXFtYEwTzVIHpedQyo,1065
10
+ terminaltables3-4.0.0.dist-info/METADATA,sha256=5y1z4s3FmlQo4RPzFMXDAeaqEiYpHxxhc35wbYlmOmY,3696
11
+ terminaltables3-4.0.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
12
+ terminaltables3-4.0.0.dist-info/entry_points.txt,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ terminaltables3-4.0.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 1.9.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
File without changes