@@ -24,15 +24,15 @@ def __init__(
2424 if header_row and body and len (body ) > 0 and len (header_row ) != len (body [0 ]):
2525 raise ValueError ("Header row and body rows must have the same length" )
2626 # check if any rows in body have a different number of columns
27- if body and len (body ) and list (filter (lambda x : len (x ) != len (body [0 ]), body )):
27+ if body and len (body ) and tuple (filter (lambda r : len (r ) != len (body [0 ]), body )):
2828 raise ValueError ("All rows in body must have the same length" )
2929
3030 # initialize fields
3131 self .__header_row = header_row
3232 self .__body = body
3333 self .__footer_row = footer_row
34- self .__columns = self .__count_columns ()
35- self .__cell_widths = [ 5 , 5 , 5 , 5 , 5 ] # TODO: make this automatic
34+ self .__columns = self .count_columns ()
35+ self .__cell_widths = self . get_column_widths ()
3636
3737 """
3838 ╔═════╦═══════════════════════╗ ABBBBBCBBBBBDBBBBBDBBBBBDBBBBBE
@@ -69,7 +69,10 @@ def __init__(
6969 "bottom_right_corner" : "╝" , # V
7070 }
7171
72- def __count_columns (self ):
72+ def count_columns (self ) -> int :
73+ """Get the number of columns in the table
74+ based on the provided header, footer, and body lists.
75+ """
7376 if self .__header_row :
7477 return len (self .__header_row )
7578 if self .__footer_row :
@@ -78,35 +81,54 @@ def __count_columns(self):
7881 return len (self .__body [0 ])
7982 return 0
8083
84+ def get_column_widths (self ) -> List [int ]:
85+ """Get the minimum number of characters needed for the values
86+ in each column in the table with 1 space of padding on each side.
87+ """
88+ col_counts = []
89+ for i in range (self .__columns ):
90+ # number of characters in column of i of header, each body row, and footer
91+ header_size = len (self .__header_row [i ]) if self .__header_row else 0
92+ body_size = (
93+ map (lambda row , i = i : len (row [i ]), self .__body ) if self .__body else [0 ]
94+ )
95+ footer_size = len (self .__footer_row [i ]) if self .__footer_row else 0
96+ # get the max and add 2 for padding each side with a space
97+ col_counts .append (max (header_size , * body_size , footer_size ) + 2 )
98+ return col_counts
99+
81100 def __pad (self , text : str , width : int , alignment : int = ALIGN_CENTER ):
82101 """Pad a string of text to a given width with specified alignment"""
83102 if alignment == ALIGN_LEFT :
103+ # pad with spaces on the end
84104 return f" { text } " + (" " * (width - len (text ) - 2 ))
85105 if alignment == ALIGN_CENTER :
106+ # pad with spaces, half on each side
86107 before = " " * ceil ((width - len (text ) - 2 ) / 2 )
87108 after = " " * floor ((width - len (text ) - 2 ) / 2 )
88109 return before + f" { text } " + after
89110 if alignment == ALIGN_RIGHT :
111+ # pad with spaces at the beginning
90112 return (" " * (width - len (text ) - 2 )) + f" { text } "
91113 raise ValueError (f"The value '{ alignment } ' is not valid for alignment." )
92114
93115 def __row_to_ascii (
94116 self ,
95- left : str ,
117+ left_edge : str ,
96118 first_col_sep : str ,
97- col_sep : str ,
98- right : str ,
119+ column_seperator : str ,
120+ right_edge : str ,
99121 filler : Union [str , List ],
100122 ) -> str :
101123 """Assembles a row of the ascii table"""
102124 # left edge of the row
103- output : str = left
125+ output = left_edge
104126 # content across the first column
105127 output += (
106- # edge or row separator if filler is specified
128+ # edge or row separator if filler is a specific character
107129 filler * self .__cell_widths [0 ]
108130 if isinstance (filler , str )
109- # otherwise, first column content
131+ # otherwise, use the first column's content
110132 else self .__pad (str (filler [0 ]), self .__cell_widths [0 ])
111133 )
112134 # separation of first column from the rest of the table
@@ -115,86 +137,86 @@ def __row_to_ascii(
115137 for i in range (1 , self .__columns ):
116138 # content between separators
117139 output += (
118- # edge or row separator if filler is specified
140+ # edge or row separator if filler is a specific character
119141 filler * self .__cell_widths [i ]
120142 if isinstance (filler , str )
121- # otherwise, column content
143+ # otherwise, use the column content
122144 else self .__pad (str (filler [i ]), self .__cell_widths [i ])
123145 )
124146 # add a separator
125- output += col_sep
147+ output += column_seperator
126148 # replace last seperator with symbol for edge of the row
127- output = output [0 :- 1 ] + right
149+ output = output [0 :- 1 ] + right_edge
128150 return output + "\n "
129151
130152 def __top_edge_to_ascii (self ) -> str :
131153 """Assembles the top edge of the ascii table"""
132154 return self .__row_to_ascii (
133- left = self .__parts ["top_left_corner" ],
155+ left_edge = self .__parts ["top_left_corner" ],
134156 first_col_sep = self .__parts ["first_col_top_tee" ],
135- col_sep = self .__parts ["top_tee" ],
136- right = self .__parts ["top_right_corner" ],
157+ column_seperator = self .__parts ["top_tee" ],
158+ right_edge = self .__parts ["top_right_corner" ],
137159 filler = self .__parts ["top_and_bottom_edge" ],
138160 )
139161
140162 def __bottom_edge_to_ascii (self ) -> str :
141163 """Assembles the top edge of the ascii table"""
142164 return self .__row_to_ascii (
143- left = self .__parts ["bottom_left_corner" ],
165+ left_edge = self .__parts ["bottom_left_corner" ],
144166 first_col_sep = self .__parts ["first_col_bottom_tee" ],
145- col_sep = self .__parts ["bottom_tee" ],
146- right = self .__parts ["bottom_right_corner" ],
167+ column_seperator = self .__parts ["bottom_tee" ],
168+ right_edge = self .__parts ["bottom_right_corner" ],
147169 filler = self .__parts ["top_and_bottom_edge" ],
148170 )
149171
150172 def __header_row_to_ascii (self ) -> str :
151173 """Assembles the header row line of the ascii table"""
152174 return self .__row_to_ascii (
153- left = self .__parts ["left_and_right_edge" ],
175+ left_edge = self .__parts ["left_and_right_edge" ],
154176 first_col_sep = self .__parts ["first_col_sep" ],
155- col_sep = self .__parts ["middle_edge" ],
156- right = self .__parts ["left_and_right_edge" ],
177+ column_seperator = self .__parts ["middle_edge" ],
178+ right_edge = self .__parts ["left_and_right_edge" ],
157179 filler = self .__header_row ,
158180 )
159181
160182 def __footer_row_to_ascii (self ) -> str :
161183 """Assembles the header row line of the ascii table"""
162184 return self .__row_to_ascii (
163- left = self .__parts ["left_and_right_edge" ],
185+ left_edge = self .__parts ["left_and_right_edge" ],
164186 first_col_sep = self .__parts ["first_col_sep" ],
165- col_sep = self .__parts ["middle_edge" ],
166- right = self .__parts ["left_and_right_edge" ],
187+ column_seperator = self .__parts ["middle_edge" ],
188+ right_edge = self .__parts ["left_and_right_edge" ],
167189 filler = self .__footer_row ,
168190 )
169191
170192 def __header_sep_to_ascii (self ) -> str :
171193 """Assembles the seperator below the header of the ascii table"""
172194 return self .__row_to_ascii (
173- left = self .__parts ["header_left_tee" ],
195+ left_edge = self .__parts ["header_left_tee" ],
174196 first_col_sep = self .__parts ["first_col_header_cross" ],
175- col_sep = self .__parts ["header_row_cross" ],
176- right = self .__parts ["header_right_tee" ],
197+ column_seperator = self .__parts ["header_row_cross" ],
198+ right_edge = self .__parts ["header_right_tee" ],
177199 filler = self .__parts ["header_row_sep" ],
178200 )
179201
180202 def __footer_sep_to_ascii (self ) -> str :
181203 """Assembles the seperator below the header of the ascii table"""
182204 return self .__row_to_ascii (
183- left = self .__parts ["footer_left_tee" ],
205+ left_edge = self .__parts ["footer_left_tee" ],
184206 first_col_sep = self .__parts ["first_col_footer_cross" ],
185- col_sep = self .__parts ["footer_row_cross" ],
186- right = self .__parts ["footer_right_tee" ],
207+ column_seperator = self .__parts ["footer_row_cross" ],
208+ right_edge = self .__parts ["footer_right_tee" ],
187209 filler = self .__parts ["footer_row_sep" ],
188210 )
189211
190212 def __body_to_ascii (self ) -> str :
191213 output : str = ""
192214 for row in self .__body :
193215 output += self .__row_to_ascii (
194- left = self .__parts ["left_and_right_edge" ],
216+ left_edge = self .__parts ["left_and_right_edge" ],
195217 first_col_sep = self .__parts ["first_col_sep" ],
196- col_sep = self .__parts ["middle_edge" ],
197- right = self .__parts ["left_and_right_edge" ],
218+ column_seperator = self .__parts ["middle_edge" ],
219+ right_edge = self .__parts ["left_and_right_edge" ],
198220 filler = row ,
199221 )
200222 return output
@@ -225,6 +247,10 @@ def table2ascii(
225247 footer_row : Optional [List ] = None ,
226248) -> str :
227249 """Convert a 2D Python table to ASCII text
228- #TODO: add param documentation
250+
251+ ### Arguments
252+ :param header_row: :class:`Optional[List]` List of column values in the table's header row
253+ :param body: :class:`Optional[List[List]]` 2-dimensional list of values in the table's body
254+ :param footer_row: :class:`Optional[List]` List of column values in the table's footer row
229255 """
230256 return TableToAscii (header_row , body , footer_row ).to_ascii ()
0 commit comments