22
33import textwrap
44from math import ceil , floor
5+ from collections .abc import Sequence
56
67from wcwidth import wcswidth
78
1516 FooterColumnCountMismatchError ,
1617 InvalidAlignmentError ,
1718 InvalidCellPaddingError ,
19+ NoHeaderBodyOrFooterError ,
1820)
1921from .merge import Merge
2022from .options import Options
@@ -27,9 +29,9 @@ class TableToAscii:
2729
2830 def __init__ (
2931 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 ,
3335 options : Options ,
3436 ):
3537 """Validate arguments and initialize fields
@@ -41,9 +43,9 @@ def __init__(
4143 options: The options for the table
4244 """
4345 # 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
4749 self .__style = options .style
4850 self .__first_col_heading = options .first_col_heading
4951 self .__last_col_heading = options .last_col_heading
@@ -60,6 +62,10 @@ def __init__(
6062 if body and any (len (row ) != self .__columns for row in body ):
6163 raise BodyColumnCountMismatchError (body , self .__columns )
6264
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+
6369 # calculate or use given column widths
6470 self .__column_widths = self .__calculate_column_widths (options .column_widths )
6571
@@ -103,7 +109,7 @@ def widest_line(value: SupportsStr) -> int:
103109 text = str (value )
104110 return max (self .__str_width (line ) for line in text .splitlines ()) if len (text ) else 0
105111
106- def get_column_width (row : list [SupportsStr ], column : int ) -> int :
112+ def get_column_width (row : Sequence [SupportsStr ], column : int ) -> int :
107113 """Get the width of a cell in a column"""
108114 value = row [column ]
109115 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:
122128 column_widths .append (max (header_size , body_size , footer_size ) + self .__cell_padding * 2 )
123129 return column_widths
124130
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 ]:
126134 """Calculate the width of each column in the table based on the cell values and provided column widths.
127135
128136 Args:
@@ -187,7 +195,7 @@ def __pad(self, cell_value: SupportsStr, width: int, alignment: Alignment) -> st
187195 raise InvalidAlignmentError (alignment )
188196
189197 def __wrap_long_lines_in_merged_cells (
190- self , row : list [SupportsStr ], column_separator : str
198+ self , row : Sequence [SupportsStr ], column_separator : str
191199 ) -> list [SupportsStr ]:
192200 """Wrap long lines in merged cells to the width of the merged cell
193201
@@ -219,9 +227,9 @@ def __row_to_ascii(
219227 heading_col_sep : str ,
220228 column_separator : str ,
221229 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 ,
225233 top_tee : str | None = None ,
226234 bottom_tee : str | None = None ,
227235 heading_col_top_tee : str | None = None ,
@@ -266,9 +274,9 @@ def __line_in_row_to_ascii(
266274 heading_col_sep : str ,
267275 column_separator : str ,
268276 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 ,
272280 top_tee : str | None = None ,
273281 bottom_tee : str | None = None ,
274282 heading_col_top_tee : str | None = None ,
@@ -306,9 +314,9 @@ def __line_in_cell_column_to_ascii(
306314 heading_col_sep : str ,
307315 column_separator : str ,
308316 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 ,
312320 top_tee : str | None = None ,
313321 bottom_tee : str | None = None ,
314322 heading_col_top_tee : str | None = None ,
@@ -373,7 +381,7 @@ def __line_in_cell_column_to_ascii(
373381 return output + sep
374382
375383 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 ]
377385 ) -> str :
378386 # If this is a merge cell, merge with the previous column
379387 if filler [col_index ] is Merge .LEFT :
@@ -437,7 +445,7 @@ def __bottom_edge_to_ascii(self) -> str:
437445 heading_col_bottom_tee = self .__style .heading_col_bottom_tee ,
438446 )
439447
440- def __content_row_to_ascii (self , row : list [SupportsStr ]) -> str :
448+ def __content_row_to_ascii (self , row : Sequence [SupportsStr ]) -> str :
441449 """Assembles a row of cell values into a single line of the ascii table
442450
443451 Returns:
@@ -453,8 +461,8 @@ def __content_row_to_ascii(self, row: list[SupportsStr]) -> str:
453461
454462 def __heading_sep_to_ascii (
455463 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 ,
458466 ) -> str :
459467 """Assembles the separator below the header or above footer of the ascii table
460468
@@ -475,7 +483,7 @@ def __heading_sep_to_ascii(
475483 heading_col_bottom_tee = self .__style .heading_col_heading_row_bottom_tee ,
476484 )
477485
478- def __body_to_ascii (self , body : list [ list [SupportsStr ]]) -> str :
486+ def __body_to_ascii (self , body : Sequence [ Sequence [SupportsStr ]]) -> str :
479487 """Assembles the body of the ascii table
480488
481489 Returns:
@@ -551,14 +559,14 @@ def to_ascii(self) -> str:
551559
552560
553561def 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 ,
557565 * ,
558566 first_col_heading : bool = False ,
559567 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 ,
562570 cell_padding : int = 1 ,
563571 style : TableStyle = PresetStyle .double_thin_compact ,
564572 use_wcwidth : bool = True ,
@@ -581,8 +589,8 @@ def table2ascii(
581589 Defaults to :py:obj:`False`.
582590 column_widths: List of widths in characters for each column. Any value of :py:obj:`None`
583591 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`.
586594 alignments: List of alignments for each column
587595 (ex. ``[Alignment.LEFT, Alignment.CENTER, Alignment.RIGHT]``). If not specified or set to
588596 :py:obj:`None`, all columns will be center-aligned. Defaults to :py:obj:`None`.
0 commit comments