@@ -600,9 +600,21 @@ def set_default_grid(grid: Grid) -> None:
600600 DEFAULT_GRID = grid
601601
602602
603- def generate_table (rows : Iterable [Union [Iterable , object ]], columns : Collection [Union [str , Tuple [str , dict ]]]= None ,
604- grid_style : Optional [Grid ]= None , transpose : bool = False ) -> str :
605- """Convenience function to easily generate a table from rows/columns"""
603+ def generate_table (rows : Iterable [Union [Iterable , object ]],
604+ columns : Collection [Union [str , Tuple [str , dict ]]]= None ,
605+ grid_style : Optional [Grid ]= None ,
606+ transpose : bool = False ,
607+ row_tagger : Callable = None ) -> str :
608+ """
609+ Convenience function to easily generate a table from rows/columns
610+
611+ :param rows: iterable of objects or iterable fields
612+ :param columns: Iterable of column definitions
613+ :param grid_style: The grid style to use
614+ :param transpose: Transpose the rows/columns for display
615+ :param row_tagger: decorator function to apply per-row options
616+ :return: formatted string containing the table
617+ """
606618 show_headers = True
607619 use_attrib = False
608620 if isinstance (columns , Collection ) and len (columns ) > 0 :
@@ -634,7 +646,7 @@ def generate_table(rows: Iterable[Union[Iterable, object]], columns: Collection[
634646 if grid_style is None :
635647 grid_style = DEFAULT_GRID
636648 formatter = TableFormatter (columns , grid_style = grid_style , show_header = show_headers ,
637- use_attribs = use_attrib , transpose = transpose )
649+ use_attribs = use_attrib , transpose = transpose , row_tagger = row_tagger )
638650 return formatter .generate_table (rows )
639651
640652
@@ -696,7 +708,7 @@ def Column(col_name: str,
696708 return col_name , opts
697709
698710
699- def Row (* args , text_color : TableColors = None ):
711+ def Row (* args , text_color : Union [ TableColors , str ] = None ):
700712 """
701713 Processes row options and generates a tuple in the format the TableFormatter expects
702714 :param args: Can be either 1 object or a list of values
@@ -750,7 +762,8 @@ def __init__(self,
750762 show_header = True ,
751763 use_attribs = False ,
752764 transpose = False ,
753- row_show_header = False ):
765+ row_show_header = False ,
766+ row_tagger : Callable = None ):
754767 """
755768 :param columns: list of either column names or tuples of (column name, dict of column options)
756769 :param cell_padding: number of spaces to pad to the left/right of each column
@@ -776,6 +789,7 @@ def __init__(self,
776789 TableFormatter .TABLE_OPT_TRANSPOSE : transpose ,
777790 TableFormatter .TABLE_OPT_ROW_HEADER : row_show_header }
778791 self ._show_header = show_header
792+ self ._row_tagger = row_tagger
779793
780794 for col_index , column in enumerate (columns ):
781795 if isinstance (column , tuple ) and len (column ) > 1 and isinstance (column [1 ], dict ):
@@ -911,11 +925,14 @@ def generate_table(self, entries: Iterable[Union[Iterable, object]], force_trans
911925 except TypeError :
912926 # not iterable, so we just use the object directly
913927 entry_obj = entry
928+ if self ._row_tagger is not None :
929+ entry_opts = self ._row_tagger (entry_obj )
914930 else :
915-
916- if len (entry ) == 2 :
917- entry_opts = entry [1 ]
918931 entry_obj = entry [0 ]
932+ if self ._row_tagger is not None :
933+ entry_opts = self ._row_tagger (entry_obj )
934+ if len (entry ) == 2 and isinstance (entry [1 ], dict ):
935+ entry_opts .update (entry [1 ])
919936
920937 for column_index , attrib_name in enumerate (self ._column_attribs ):
921938 field_obj = None
@@ -928,8 +945,8 @@ def generate_table(self, entries: Iterable[Union[Iterable, object]], force_trans
928945 formatter = self ._get_column_option (column_index , TableFormatter .COL_OPT_FIELD_FORMATTER )
929946 obj_formatter = self ._get_column_option (column_index , TableFormatter .COL_OPT_OBJECT_FORMATTER )
930947 if obj_formatter is not None and callable (obj_formatter ):
931- field_string = obj_formatter (entry_obj )
932- elif formatter is not None and callable (formatter ):
948+ field_obj = obj_formatter (entry_obj )
949+ if formatter is not None and callable (formatter ):
933950 field_string = formatter (field_obj )
934951 elif isinstance (field_obj , str ):
935952 field_string = field_obj
@@ -949,18 +966,20 @@ def generate_table(self, entries: Iterable[Union[Iterable, object]], force_trans
949966 row .append (field_lines )
950967
951968 else :
952- if len (entry ) == len (self ._columns ) + 1 :
969+ if self ._row_tagger is not None :
970+ entry_opts = self ._row_tagger (entry )
971+ if len (entry ) == len (self ._columns ) + 1 and isinstance (entry [len (self ._columns )], dict ):
953972 # if there is exactly 1 more entry than columns, the last one is metadata
954- entry_opts = entry [len (self ._columns )]
973+ entry_opts . update ( entry [len (self ._columns )])
955974
956975 for column_index , field in enumerate (entry ):
957976 # skip extra values beyond the columns configured
958977 if column_index < len (self ._columns ):
959978 formatter = self ._get_column_option (column_index , TableFormatter .COL_OPT_FIELD_FORMATTER )
960979 obj_formatter = self ._get_column_option (column_index , TableFormatter .COL_OPT_OBJECT_FORMATTER )
961980 if obj_formatter is not None and callable (obj_formatter ):
962- field_string = obj_formatter (entry )
963- elif formatter is not None and callable (formatter ):
981+ field = obj_formatter (entry )
982+ if formatter is not None and callable (formatter ):
964983 field_string = formatter (field , )
965984 elif isinstance (field , str ):
966985 field_string = field
@@ -1202,6 +1221,10 @@ def generate_table(self, entries: Iterable[Union[Iterable, object]], force_trans
12021221 out_string += self ._grid_style .border_right_row_divider
12031222 out_string += '\n '
12041223
1224+ opts = dict ()
1225+ if row_index < len (row_opts ):
1226+ opts = row_opts [row_index ]
1227+
12051228 # loop for each line in the row
12061229 num_lines = row_counts [row_index ]
12071230 for row_line in range (0 , num_lines ):
@@ -1228,23 +1251,25 @@ def generate_table(self, entries: Iterable[Union[Iterable, object]], force_trans
12281251 else :
12291252 mapped_line = row_line - int ((num_lines - len (field )) / 2 )
12301253
1231- opts = dict ()
12321254 if len (field ) > mapped_line >= 0 :
1233- row_line_text = field [mapped_line ]
1234- if row_index < len (row_opts ):
1235- opts = row_opts [row_index ]
12361255 # if the field has a line for the current row line, add it
1256+ row_line_text = field [mapped_line ]
12371257 else :
12381258 row_line_text = ''
1259+
1260+ colorize = len (row_line_text .strip ()) > 0 and TableFormatter .ROW_OPT_TEXT_COLOR in opts .keys ()
1261+
12391262 row_line_text = _pad_columns (row_line_text ,
12401263 pad_char = self ._grid_style .cell_pad_char ,
12411264 align = halign_cells [col_index ],
12421265 width = col_widths [col_index ])
12431266
1244- if TableFormatter . ROW_OPT_TEXT_COLOR in opts . keys () :
1267+ if colorize :
12451268 row_line_text = opts [TableFormatter .ROW_OPT_TEXT_COLOR ] + row_line_text
12461269 out_string += row_line_text
1247- out_string += pad_string + TableColors .RESET
1270+ out_string += pad_string
1271+ if colorize :
1272+ out_string += TableColors .RESET
12481273
12491274 if self ._grid_style .border_right :
12501275 out_string += self ._grid_style .border_right_span (row_index )
0 commit comments