diff --git a/table2ascii/alignment.py b/table2ascii/alignment.py index e705f3d..68f48d5 100644 --- a/table2ascii/alignment.py +++ b/table2ascii/alignment.py @@ -2,8 +2,7 @@ class Alignment(Enum): - """ - Enum for text alignment types within a table cell + """Enum for text alignment types within a table cell Example:: diff --git a/table2ascii/preset_style.py b/table2ascii/preset_style.py index 4ef96b0..486ea1c 100644 --- a/table2ascii/preset_style.py +++ b/table2ascii/preset_style.py @@ -2,8 +2,7 @@ class PresetStyle: - """ - Importable preset styles for more easily selecting a :ref:`TableStyle`. + """Importable preset styles for more easily selecting a :ref:`TableStyle`. See the :ref:`Preset Styles` for more information on the available styles. diff --git a/table2ascii/table_style.py b/table2ascii/table_style.py index 323c7d2..53cc4b5 100644 --- a/table2ascii/table_style.py +++ b/table2ascii/table_style.py @@ -61,8 +61,7 @@ class TableStyle: @classmethod def from_string(cls, string: str) -> "TableStyle": - """ - Create a TableStyle from a string + """Create a TableStyle from a string Args: string: The string to create the TableStyle from @@ -77,8 +76,7 @@ def from_string(cls, string: str) -> "TableStyle": return cls(*string) def set(self, **kwargs) -> "TableStyle": - """ - Set attributes of the TableStyle + """Set attributes of the TableStyle Args: kwargs: The attributes to set diff --git a/table2ascii/table_to_ascii.py b/table2ascii/table_to_ascii.py index a16dffc..70359c8 100644 --- a/table2ascii/table_to_ascii.py +++ b/table2ascii/table_to_ascii.py @@ -19,8 +19,7 @@ def __init__( footer: list[SupportsStr] | None, options: Options, ): - """ - Validate arguments and initialize fields + """Validate arguments and initialize fields Args: header: The values in the header of the table @@ -50,22 +49,7 @@ def __init__( ) # calculate or use given column widths - self.__column_widths = self.__auto_column_widths() - if options.column_widths: - # check that the right number of columns were specified - if len(options.column_widths) != self.__columns: - raise ValueError("Length of `column_widths` list must equal the number of columns") - # check that each column is at least as large as the minimum size - for i in range(len(options.column_widths)): - option = options.column_widths[i] - minimum = self.__column_widths[i] - if option is None: - option = minimum - elif option < minimum: - raise ValueError( - f"The value at index {i} of `column_widths` is {option} which is less than the minimum {minimum}." - ) - self.__column_widths[i] = option + self.__column_widths = self.__calculate_column_widths(options.column_widths) self.__alignments = options.alignments or [Alignment.CENTER] * self.__columns @@ -78,9 +62,7 @@ def __init__( raise ValueError("Cell padding must be greater than or equal to 0") def __count_columns(self) -> int: - """ - Get the number of columns in the table based on the - provided header, footer, and body lists. + """Get the number of columns in the table based on the provided header, footer, and body lists. Returns: The number of columns in the table @@ -94,9 +76,8 @@ def __count_columns(self) -> int: return 0 def __auto_column_widths(self) -> list[int]: - """ - Get the minimum number of characters needed for the values in - each column in the table with 1 space of padding on each side. + """Get the minimum number of characters needed for the values in each column in the table + with 1 space of padding on each side. Returns: The minimum number of characters needed for each column @@ -118,9 +99,35 @@ def widest_line(value: SupportsStr) -> int: column_widths.append(max(header_size, body_size, footer_size) + self.__cell_padding * 2) return column_widths - def __pad(self, cell_value: SupportsStr, width: int, alignment: Alignment) -> str: + def __calculate_column_widths(self, user_column_widths: list[int | None] | None) -> list[int]: + """Calculate the width of each column in the table based on the cell values and provided column widths. + + Args: + user_column_widths: The user specified column widths + + Returns: + The width of each column in the table """ - Pad a string of text to a given width with specified alignment + column_widths = self.__auto_column_widths() + if user_column_widths: + # check that the right number of columns were specified + if len(user_column_widths) != self.__columns: + raise ValueError("Length of `column_widths` list must equal the number of columns") + # check that each column is at least as large as the minimum size + for i in range(len(user_column_widths)): + option = user_column_widths[i] + minimum = column_widths[i] + if option is None: + option = minimum + elif option < minimum: + raise ValueError( + f"The value at index {i} of `column_widths` is {option} which is less than the minimum {minimum}." + ) + column_widths[i] = option + return column_widths + + def __pad(self, cell_value: SupportsStr, width: int, alignment: Alignment) -> str: + """Pad a string of text to a given width with specified alignment Args: cell_value: The text in the cell to pad @@ -154,8 +161,7 @@ def __row_to_ascii( right_edge: str, filler: str | list[SupportsStr], ) -> str: - """ - Assembles a line of text in the ascii table + """Assembles a line of text in the ascii table Returns: The line in the ascii table @@ -165,50 +171,96 @@ def __row_to_ascii( num_lines = max(len(str(cell).splitlines()) for cell in filler) or 1 # repeat for each line of text in the cell for line_index in range(num_lines): - # left edge of the row - output += left_edge - # add columns - for col_index in range(self.__columns): - # content between separators - col_content = "" - # if filler is a separator character, repeat it for the full width of the column - if isinstance(filler, str): - col_content = filler * self.__column_widths[col_index] - # otherwise, use the text from the corresponding column in the filler list - else: - # get the text of the current line in the cell - # if there are fewer lines in the current cell than others, empty string is used - col_lines = str(filler[col_index]).splitlines() - if line_index < len(col_lines): - col_content = col_lines[line_index] - # pad the text to the width of the column using the alignment - col_content = self.__pad( - col_content, - self.__column_widths[col_index], - self.__alignments[col_index], - ) - output += col_content - # column separator - sep = column_separator - if col_index == 0 and self.__first_col_heading: - # use column heading if first column option is specified - sep = heading_col_sep - elif col_index == self.__columns - 2 and self.__last_col_heading: - # use column heading if last column option is specified - sep = heading_col_sep - elif col_index == self.__columns - 1: - # replace last separator with symbol for edge of the row - sep = right_edge - output += sep - output += "\n" - # don't use separation row if it's only space - if isinstance(filler, str) and output.strip() == "": - output = "" + output += self.__line_in_row_to_ascii( + line_index, + left_edge, + heading_col_sep, + column_separator, + right_edge, + filler, + ) + # don't use separation row if it's only space + if isinstance(filler, str) and output.strip() == "": + output = "" return output - def __top_edge_to_ascii(self) -> str: + def __line_in_row_to_ascii( + self, + line_index: int, + left_edge: str, + heading_col_sep: str, + column_separator: str, + right_edge: str, + filler: str | list[SupportsStr], + ) -> str: + """Assembles a line of text in the ascii table + + Returns: + The line in the ascii table + """ + output = left_edge + # add columns + for col_index in range(self.__columns): + output += self.__line_in_cell_column_to_ascii( + line_index, + col_index, + heading_col_sep, + column_separator, + right_edge, + filler, + ) + output += "\n" + return output + + def __line_in_cell_column_to_ascii( + self, + line_index: int, + col_index: int, + heading_col_sep: str, + column_separator: str, + right_edge: str, + filler: str | list[SupportsStr], + ) -> str: + """Assembles a column of text in the ascii table + + Returns: + The column in the ascii table """ - Assembles the top edge of the ascii table + output = "" + # content between separators + col_content = "" + # if filler is a separator character, repeat it for the full width of the column + if isinstance(filler, str): + col_content = filler * self.__column_widths[col_index] + # otherwise, use the text from the corresponding column in the filler list + else: + # get the text of the current line in the cell + # if there are fewer lines in the current cell than others, empty string is used + col_lines = str(filler[col_index]).splitlines() + if line_index < len(col_lines): + col_content = col_lines[line_index] + # pad the text to the width of the column using the alignment + col_content = self.__pad( + col_content, + self.__column_widths[col_index], + self.__alignments[col_index], + ) + output += col_content + # column separator + sep = column_separator + if col_index == 0 and self.__first_col_heading: + # use column heading if first column option is specified + sep = heading_col_sep + elif col_index == self.__columns - 2 and self.__last_col_heading: + # use column heading if last column option is specified + sep = heading_col_sep + elif col_index == self.__columns - 1: + # replace last separator with symbol for edge of the row + sep = right_edge + return output + sep + + def __top_edge_to_ascii(self) -> str: + """Assembles the top edge of the ascii table Returns: The top edge of the ascii table @@ -222,8 +274,7 @@ def __top_edge_to_ascii(self) -> str: ) def __bottom_edge_to_ascii(self) -> str: - """ - Assembles the bottom edge of the ascii table + """Assembles the bottom edge of the ascii table Returns: The bottom edge of the ascii table @@ -237,8 +288,7 @@ def __bottom_edge_to_ascii(self) -> str: ) def __heading_row_to_ascii(self, row: list[SupportsStr]) -> str: - """ - Assembles the header or footer row line of the ascii table + """Assembles the header or footer row line of the ascii table Returns: The header or footer row line of the ascii table @@ -252,8 +302,7 @@ def __heading_row_to_ascii(self, row: list[SupportsStr]) -> str: ) def __heading_sep_to_ascii(self) -> str: - """ - Assembles the separator below the header or above footer of the ascii table + """Assembles the separator below the header or above footer of the ascii table Returns: The separator line @@ -267,8 +316,7 @@ def __heading_sep_to_ascii(self) -> str: ) def __body_to_ascii(self, body: list[list[SupportsStr]]) -> str: - """ - Assembles the body of the ascii table + """Assembles the body of the ascii table Returns: The body of the ascii table @@ -292,8 +340,7 @@ def __body_to_ascii(self, body: list[list[SupportsStr]]) -> str: ) def to_ascii(self) -> str: - """ - Generates a formatted ASCII table + """Generates a formatted ASCII table Returns: The generated ASCII table @@ -329,8 +376,7 @@ def table2ascii( cell_padding: int = 1, style: TableStyle = PresetStyle.double_thin_compact, ) -> str: - """ - Convert a 2D Python table to ASCII text + """Convert a 2D Python table to ASCII text Args: header: List of column values in the table's header row. All values should be :class:`str`