2
2
3
3
import textwrap
4
4
from math import ceil , floor
5
+ from collections .abc import Sequence
5
6
6
7
from wcwidth import wcswidth
7
8
15
16
FooterColumnCountMismatchError ,
16
17
InvalidAlignmentError ,
17
18
InvalidCellPaddingError ,
19
+ NoHeaderBodyOrFooterError ,
18
20
)
19
21
from .merge import Merge
20
22
from .options import Options
@@ -27,9 +29,9 @@ class TableToAscii:
27
29
28
30
def __init__ (
29
31
self ,
30
- header : list [SupportsStr ] | None ,
31
- body : list [ list [SupportsStr ]] | None ,
32
- footer : list [SupportsStr ] | None ,
32
+ header : Sequence [SupportsStr ] | None ,
33
+ body : Sequence [ Sequence [SupportsStr ]] | None ,
34
+ footer : Sequence [SupportsStr ] | None ,
33
35
options : Options ,
34
36
):
35
37
"""Validate arguments and initialize fields
@@ -41,9 +43,9 @@ def __init__(
41
43
options: The options for the table
42
44
"""
43
45
# initialize fields
44
- self .__header = header
45
- self .__body = body
46
- self .__footer = footer
46
+ self .__header = list ( header ) if header else None
47
+ self .__body = list ([ list ( row ) for row in body ]) if body else None
48
+ self .__footer = list ( footer ) if footer else None
47
49
self .__style = options .style
48
50
self .__first_col_heading = options .first_col_heading
49
51
self .__last_col_heading = options .last_col_heading
@@ -60,6 +62,10 @@ def __init__(
60
62
if body and any (len (row ) != self .__columns for row in body ):
61
63
raise BodyColumnCountMismatchError (body , self .__columns )
62
64
65
+ # check that at least one of header, body, or footer is not None
66
+ if not header and not body and not footer :
67
+ raise NoHeaderBodyOrFooterError ()
68
+
63
69
# calculate or use given column widths
64
70
self .__column_widths = self .__calculate_column_widths (options .column_widths )
65
71
@@ -103,7 +109,7 @@ def widest_line(value: SupportsStr) -> int:
103
109
text = str (value )
104
110
return max (self .__str_width (line ) for line in text .splitlines ()) if len (text ) else 0
105
111
106
- def get_column_width (row : list [SupportsStr ], column : int ) -> int :
112
+ def get_column_width (row : Sequence [SupportsStr ], column : int ) -> int :
107
113
"""Get the width of a cell in a column"""
108
114
value = row [column ]
109
115
next_value = row [column + 1 ] if column < self .__columns - 1 else None
@@ -122,7 +128,9 @@ def get_column_width(row: list[SupportsStr], column: int) -> int:
122
128
column_widths .append (max (header_size , body_size , footer_size ) + self .__cell_padding * 2 )
123
129
return column_widths
124
130
125
- def __calculate_column_widths (self , user_column_widths : list [int | None ] | None ) -> list [int ]:
131
+ def __calculate_column_widths (
132
+ self , user_column_widths : Sequence [int | None ] | None
133
+ ) -> list [int ]:
126
134
"""Calculate the width of each column in the table based on the cell values and provided column widths.
127
135
128
136
Args:
@@ -187,7 +195,7 @@ def __pad(self, cell_value: SupportsStr, width: int, alignment: Alignment) -> st
187
195
raise InvalidAlignmentError (alignment )
188
196
189
197
def __wrap_long_lines_in_merged_cells (
190
- self , row : list [SupportsStr ], column_separator : str
198
+ self , row : Sequence [SupportsStr ], column_separator : str
191
199
) -> list [SupportsStr ]:
192
200
"""Wrap long lines in merged cells to the width of the merged cell
193
201
@@ -219,9 +227,9 @@ def __row_to_ascii(
219
227
heading_col_sep : str ,
220
228
column_separator : str ,
221
229
right_edge : str ,
222
- filler : str | list [SupportsStr ],
223
- previous_content_row : list [SupportsStr ] | None = None ,
224
- next_content_row : list [SupportsStr ] | None = None ,
230
+ filler : str | Sequence [SupportsStr ],
231
+ previous_content_row : Sequence [SupportsStr ] | None = None ,
232
+ next_content_row : Sequence [SupportsStr ] | None = None ,
225
233
top_tee : str | None = None ,
226
234
bottom_tee : str | None = None ,
227
235
heading_col_top_tee : str | None = None ,
@@ -266,9 +274,9 @@ def __line_in_row_to_ascii(
266
274
heading_col_sep : str ,
267
275
column_separator : str ,
268
276
right_edge : str ,
269
- filler : str | list [SupportsStr ],
270
- previous_content_row : list [SupportsStr ] | None = None ,
271
- next_content_row : list [SupportsStr ] | None = None ,
277
+ filler : str | Sequence [SupportsStr ],
278
+ previous_content_row : Sequence [SupportsStr ] | None = None ,
279
+ next_content_row : Sequence [SupportsStr ] | None = None ,
272
280
top_tee : str | None = None ,
273
281
bottom_tee : str | None = None ,
274
282
heading_col_top_tee : str | None = None ,
@@ -306,9 +314,9 @@ def __line_in_cell_column_to_ascii(
306
314
heading_col_sep : str ,
307
315
column_separator : str ,
308
316
right_edge : str ,
309
- filler : str | list [SupportsStr ],
310
- previous_content_row : list [SupportsStr ] | None = None ,
311
- next_content_row : list [SupportsStr ] | None = None ,
317
+ filler : str | Sequence [SupportsStr ],
318
+ previous_content_row : Sequence [SupportsStr ] | None = None ,
319
+ next_content_row : Sequence [SupportsStr ] | None = None ,
312
320
top_tee : str | None = None ,
313
321
bottom_tee : str | None = None ,
314
322
heading_col_top_tee : str | None = None ,
@@ -373,7 +381,7 @@ def __line_in_cell_column_to_ascii(
373
381
return output + sep
374
382
375
383
def __get_padded_cell_line_content (
376
- self , line_index : int , col_index : int , column_separator : str , filler : list [SupportsStr ]
384
+ self , line_index : int , col_index : int , column_separator : str , filler : Sequence [SupportsStr ]
377
385
) -> str :
378
386
# If this is a merge cell, merge with the previous column
379
387
if filler [col_index ] is Merge .LEFT :
@@ -437,7 +445,7 @@ def __bottom_edge_to_ascii(self) -> str:
437
445
heading_col_bottom_tee = self .__style .heading_col_bottom_tee ,
438
446
)
439
447
440
- def __content_row_to_ascii (self , row : list [SupportsStr ]) -> str :
448
+ def __content_row_to_ascii (self , row : Sequence [SupportsStr ]) -> str :
441
449
"""Assembles a row of cell values into a single line of the ascii table
442
450
443
451
Returns:
@@ -453,8 +461,8 @@ def __content_row_to_ascii(self, row: list[SupportsStr]) -> str:
453
461
454
462
def __heading_sep_to_ascii (
455
463
self ,
456
- previous_content_row : list [SupportsStr ] | None = None ,
457
- next_content_row : list [SupportsStr ] | None = None ,
464
+ previous_content_row : Sequence [SupportsStr ] | None = None ,
465
+ next_content_row : Sequence [SupportsStr ] | None = None ,
458
466
) -> str :
459
467
"""Assembles the separator below the header or above footer of the ascii table
460
468
@@ -475,7 +483,7 @@ def __heading_sep_to_ascii(
475
483
heading_col_bottom_tee = self .__style .heading_col_heading_row_bottom_tee ,
476
484
)
477
485
478
- def __body_to_ascii (self , body : list [ list [SupportsStr ]]) -> str :
486
+ def __body_to_ascii (self , body : Sequence [ Sequence [SupportsStr ]]) -> str :
479
487
"""Assembles the body of the ascii table
480
488
481
489
Returns:
@@ -551,14 +559,14 @@ def to_ascii(self) -> str:
551
559
552
560
553
561
def table2ascii (
554
- header : list [SupportsStr ] | None = None ,
555
- body : list [ list [SupportsStr ]] | None = None ,
556
- footer : list [SupportsStr ] | None = None ,
562
+ header : Sequence [SupportsStr ] | None = None ,
563
+ body : Sequence [ Sequence [SupportsStr ]] | None = None ,
564
+ footer : Sequence [SupportsStr ] | None = None ,
557
565
* ,
558
566
first_col_heading : bool = False ,
559
567
last_col_heading : bool = False ,
560
- column_widths : list [int | None ] | None = None ,
561
- alignments : list [Alignment ] | None = None ,
568
+ column_widths : Sequence [int | None ] | None = None ,
569
+ alignments : Sequence [Alignment ] | None = None ,
562
570
cell_padding : int = 1 ,
563
571
style : TableStyle = PresetStyle .double_thin_compact ,
564
572
use_wcwidth : bool = True ,
@@ -581,8 +589,8 @@ def table2ascii(
581
589
Defaults to :py:obj:`False`.
582
590
column_widths: List of widths in characters for each column. Any value of :py:obj:`None`
583
591
indicates that the column width should be determined automatically. If :py:obj:`None`
584
- is passed instead of a :class:`list `, all columns will be automatically sized.
585
- Defaults to :py:obj:`None`.
592
+ is passed instead of a :class:`~collections.abc.Sequence `, all columns will be automatically
593
+ sized. Defaults to :py:obj:`None`.
586
594
alignments: List of alignments for each column
587
595
(ex. ``[Alignment.LEFT, Alignment.CENTER, Alignment.RIGHT]``). If not specified or set to
588
596
:py:obj:`None`, all columns will be center-aligned. Defaults to :py:obj:`None`.
0 commit comments