@@ -67,16 +67,12 @@ def __init__(
6767 if not header and not body and not footer :
6868 raise NoHeaderBodyOrFooterError ()
6969
70- alignments = options . alignments if options . alignments is not None else Alignment . CENTER
71-
72- # if alignments is a single Alignment, convert it to a list of that Alignment
73- self .__alignments : list [ Alignment ] = (
74- [ alignments ] * self . __columns if isinstance ( alignments , Alignment ) else list ( alignments )
70+ self . __alignments = self . __determine_alignments (
71+ options . alignments , default = Alignment . CENTER
72+ )
73+ self .__number_alignments = self . __determine_alignments (
74+ options . number_alignments , default = self . __alignments
7575 )
76-
77- # check if alignments specified have a different number of columns
78- if len (self .__alignments ) != self .__columns :
79- raise AlignmentCountMismatchError (self .__alignments , self .__columns )
8076
8177 # keep track of the number widths and positions of the decimal points for decimal alignment
8278 decimal_widths , decimal_positions = self .__calculate_decimal_widths_and_positions ()
@@ -107,6 +103,33 @@ def __count_columns(self) -> int:
107103 return len (self .__body [0 ])
108104 return 0
109105
106+ def __determine_alignments (
107+ self ,
108+ user_alignments : Sequence [Alignment ] | Alignment | None ,
109+ * ,
110+ default : Sequence [Alignment ] | Alignment ,
111+ ) -> list [Alignment ]:
112+ """Determine the alignments for each column based on the user provided alignments option.
113+
114+ Args:
115+ user_alignments: The alignments specified by the user
116+ default: The default alignments to use if user_alignments is None
117+
118+ Returns:
119+ The alignments for each column in the table
120+ """
121+ alignments = user_alignments if user_alignments is not None else default
122+
123+ # if alignments is a single Alignment, convert it to a list of that Alignment
124+ if isinstance (alignments , Alignment ):
125+ alignments = [alignments ] * self .__columns
126+
127+ # check if alignments specified have a different number of columns
128+ if len (alignments ) != self .__columns :
129+ raise AlignmentCountMismatchError (alignments , self .__columns )
130+
131+ return list (alignments )
132+
110133 def __auto_column_widths (self ) -> list [int ]:
111134 """Get the minimum number of characters needed for the values in each column in the table
112135 with 1 space of padding on each side.
@@ -150,7 +173,8 @@ def __calculate_decimal_widths_and_positions(self) -> tuple[list[int], list[int]
150173 decimal_widths : list [int ] = [0 ] * self .__columns
151174 decimal_positions : list [int ] = [0 ] * self .__columns
152175 for i in range (self .__columns ):
153- if self .__alignments [i ] != Alignment .DECIMAL :
176+ # skip if the column is not decimal aligned
177+ if self .__number_alignments [i ] != Alignment .DECIMAL :
154178 continue
155179 # list all values in the i-th column of header, body, and footer
156180 values = [str (self .__header [i ])] if self .__header else []
@@ -227,15 +251,20 @@ def __pad(self, cell_value: SupportsStr, width: int, col_index: int) -> str:
227251 """
228252 alignment = self .__alignments [col_index ]
229253 text = str (cell_value )
230- # if using decimal alignment, pad such that the decimal point
231- # is aligned to the column's decimal position
232- if alignment == Alignment .DECIMAL and self .__is_number (text ):
233- decimal_position = self .__decimal_positions [col_index ]
234- decimal_max_width = self .__decimal_widths [col_index ]
235- text_before_decimal = self .__split_decimal (text )[0 ]
236- before = " " * (decimal_position - self .__str_width (text_before_decimal ))
237- after = " " * (decimal_max_width - self .__str_width (text ) - len (before ))
238- text = f"{ before } { text } { after } "
254+ # set alignment for numeric values
255+ if self .__is_number (text ):
256+ # if the number alignment is decimal, pad such that the decimal point
257+ # is aligned to the column's decimal position and use the default alignment
258+ if self .__number_alignments [col_index ] == Alignment .DECIMAL :
259+ decimal_position = self .__decimal_positions [col_index ]
260+ decimal_max_width = self .__decimal_widths [col_index ]
261+ text_before_decimal = self .__split_decimal (text )[0 ]
262+ before = " " * (decimal_position - self .__str_width (text_before_decimal ))
263+ after = " " * (decimal_max_width - self .__str_width (text ) - len (before ))
264+ text = f"{ before } { text } { after } "
265+ # otherwise use the number alignment as the alignment for the cell
266+ else :
267+ alignment = self .__number_alignments [col_index ]
239268 # add minimum cell padding around the text
240269 padding = " " * self .__cell_padding
241270 padded_text = f"{ padding } { text } { padding } "
@@ -640,6 +669,7 @@ def table2ascii(
640669 last_col_heading : bool = False ,
641670 column_widths : Sequence [int | None ] | None = None ,
642671 alignments : Sequence [Alignment ] | Alignment | None = None ,
672+ number_alignments : Sequence [Alignment ] | Alignment | None = None ,
643673 cell_padding : int = 1 ,
644674 style : TableStyle = PresetStyle .double_thin_compact ,
645675 use_wcwidth : bool = True ,
@@ -666,6 +696,17 @@ def table2ascii(
666696 or a single alignment to apply to all columns (ex. ``Alignment.LEFT``).
667697 If not specified or set to :py:obj:`None`, all columns will be center-aligned.
668698 Defaults to :py:obj:`None`.
699+
700+ .. versionchanged:: 1.1.0
701+ ``alignments`` can now also be specified as a single :class:`Alignment` value to apply to all columns.
702+ number_alignments: List of alignments for numeric values in each column or a single alignment
703+ to apply to all columns. This argument can be used to override the alignment of numbers and
704+ is ignored for non-numeric values. Numeric values include integers, floats, and strings containing only
705+ :meth:`decimal <str.isdecimal>` characters and at most one decimal point.
706+ If not specified or set to :py:obj:`None`, numbers will be aligned based on the ``alignments`` argument.
707+ Defaults to :py:obj:`None`.
708+
709+ .. versionadded:: 1.1.0
669710 cell_padding: The minimum number of spaces to add between the cell content and the column
670711 separator. If set to ``0``, the cell content will be flush against the column separator.
671712 Defaults to ``1``.
@@ -677,13 +718,7 @@ def table2ascii(
677718 zero-width space, etc.), whereas :func:`len` determines the width solely based on the number of
678719 characters in the string. Defaults to :py:obj:`True`.
679720
680- .. versionchanged:: 1.1.0
681-
682- ``alignments`` can now also be specified as a single :class:`Alignment` value to apply to all columns.
683-
684- .. versionchanged:: 1.0.0
685-
686- Added the ``use_wcwidth`` parameter defaulting to :py:obj:`True`.
721+ .. versionadded:: 1.0.0
687722
688723 Returns:
689724 The generated ASCII table
@@ -697,6 +732,7 @@ def table2ascii(
697732 last_col_heading = last_col_heading ,
698733 column_widths = column_widths ,
699734 alignments = alignments ,
735+ number_alignments = number_alignments ,
700736 cell_padding = cell_padding ,
701737 style = style ,
702738 use_wcwidth = use_wcwidth ,
0 commit comments