@@ -67,16 +67,12 @@ def __init__(
67
67
if not header and not body and not footer :
68
68
raise NoHeaderBodyOrFooterError ()
69
69
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
75
75
)
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 )
80
76
81
77
# keep track of the number widths and positions of the decimal points for decimal alignment
82
78
decimal_widths , decimal_positions = self .__calculate_decimal_widths_and_positions ()
@@ -107,6 +103,33 @@ def __count_columns(self) -> int:
107
103
return len (self .__body [0 ])
108
104
return 0
109
105
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
+
110
133
def __auto_column_widths (self ) -> list [int ]:
111
134
"""Get the minimum number of characters needed for the values in each column in the table
112
135
with 1 space of padding on each side.
@@ -150,7 +173,8 @@ def __calculate_decimal_widths_and_positions(self) -> tuple[list[int], list[int]
150
173
decimal_widths : list [int ] = [0 ] * self .__columns
151
174
decimal_positions : list [int ] = [0 ] * self .__columns
152
175
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 :
154
178
continue
155
179
# list all values in the i-th column of header, body, and footer
156
180
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:
227
251
"""
228
252
alignment = self .__alignments [col_index ]
229
253
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 ]
239
268
# add minimum cell padding around the text
240
269
padding = " " * self .__cell_padding
241
270
padded_text = f"{ padding } { text } { padding } "
@@ -640,6 +669,7 @@ def table2ascii(
640
669
last_col_heading : bool = False ,
641
670
column_widths : Sequence [int | None ] | None = None ,
642
671
alignments : Sequence [Alignment ] | Alignment | None = None ,
672
+ number_alignments : Sequence [Alignment ] | Alignment | None = None ,
643
673
cell_padding : int = 1 ,
644
674
style : TableStyle = PresetStyle .double_thin_compact ,
645
675
use_wcwidth : bool = True ,
@@ -666,6 +696,17 @@ def table2ascii(
666
696
or a single alignment to apply to all columns (ex. ``Alignment.LEFT``).
667
697
If not specified or set to :py:obj:`None`, all columns will be center-aligned.
668
698
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
669
710
cell_padding: The minimum number of spaces to add between the cell content and the column
670
711
separator. If set to ``0``, the cell content will be flush against the column separator.
671
712
Defaults to ``1``.
@@ -677,13 +718,7 @@ def table2ascii(
677
718
zero-width space, etc.), whereas :func:`len` determines the width solely based on the number of
678
719
characters in the string. Defaults to :py:obj:`True`.
679
720
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
687
722
688
723
Returns:
689
724
The generated ASCII table
@@ -697,6 +732,7 @@ def table2ascii(
697
732
last_col_heading = last_col_heading ,
698
733
column_widths = column_widths ,
699
734
alignments = alignments ,
735
+ number_alignments = number_alignments ,
700
736
cell_padding = cell_padding ,
701
737
style = style ,
702
738
use_wcwidth = use_wcwidth ,
0 commit comments