Skip to content

Commit dfbca8c

Browse files
authored
feat: Ability to align all columns with a single Alignment (#91)
1 parent 3e8b7ca commit dfbca8c

File tree

6 files changed

+94
-25
lines changed

6 files changed

+94
-25
lines changed

README.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ output = table2ascii(
123123
body=[["10", "30", "40", "35"], ["20", "10", "20", "5"]],
124124
style=PresetStyle.plain,
125125
cell_padding=0,
126-
alignments=[Alignment.LEFT] * 4,
126+
alignments=Alignment.LEFT,
127127
)
128128

129129
print(output)
@@ -203,18 +203,18 @@ All parameters are optional. At least one of `header`, `body`, and `footer` must
203203

204204
Refer to the [documentation](https://table2ascii.readthedocs.io/en/stable/api.html#table2ascii) for more information.
205205

206-
| Option | Type | Default | Description |
207-
| :-----------------: | :----------------------------: | :-------------------: | :--------------------------------------------------------------------------------------------------: |
208-
| `header` | `Sequence[SupportsStr]` | `None` | First table row seperated by header row separator. Values should support `str()` |
209-
| `body` | `Sequence[Sequence[Sequence]]` | `None` | 2D List of rows for the main section of the table. Values should support `str()` |
210-
| `footer` | `Sequence[Sequence]` | `None` | Last table row seperated by header row separator. Values should support `str()` |
211-
| `column_widths` | `Sequence[Optional[int]]` | `None` (automatic) | List of column widths in characters for each column |
212-
| `alignments` | `Sequence[Alignment]` | `None` (all centered) | Column alignments<br/>(ex. `[Alignment.LEFT, Alignment.CENTER, Alignment.RIGHT, Alignment.DECIMAL]`) |
213-
| `style` | `TableStyle` | `double_thin_compact` | Table style to use for the table\* |
214-
| `first_col_heading` | `bool` | `False` | Whether to add a heading column separator after the first column |
215-
| `last_col_heading` | `bool` | `False` | Whether to add a heading column separator before the last column |
216-
| `cell_padding` | `int` | `1` | The minimum number of spaces to add between the cell content and the cell border |
217-
| `use_wcwidth` | `bool` | `True` | Whether to use [wcwidth][wcwidth] instead of `len()` to calculate cell width |
206+
| Option | Supported Types | Description |
207+
| :-----------------: | :-----------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------: |
208+
| `header` | `Sequence[SupportsStr]`, `None`<br/>(Default: `None`) | First table row seperated by header row separator. Values should support `str()` |
209+
| `body` | `Sequence[Sequence[SupportsStr]]`, `None`<br/>(Default: `None`) | 2D List of rows for the main section of the table. Values should support `str()` |
210+
| `footer` | `Sequence[SupportsStr]`, `None`<br/>(Default: `None`) | Last table row seperated by header row separator. Values should support `str()` |
211+
| `column_widths` | `Sequence[Optional[int]]`, `None`<br/>(Default: `None` / automatic) | List of column widths in characters for each column |
212+
| `alignments` | `Sequence[Alignment]`, `Alignment`, `None`<br/>(Default: `None` / all centered) | Column alignments<br/>(ex. `[Alignment.LEFT, Alignment.CENTER, Alignment.RIGHT, Alignment.DECIMAL]`) |
213+
| `style` | `TableStyle`<br/>(Default: `double_thin_compact`) | Table style to use for the table\* |
214+
| `first_col_heading` | `bool`<br/>(Default: `False`) | Whether to add a heading column separator after the first column |
215+
| `last_col_heading` | `bool`<br/>(Default: `False`) | Whether to add a heading column separator before the last column |
216+
| `cell_padding` | `int`<br/>(Default: `1`) | The minimum number of spaces to add between the cell content and the cell border |
217+
| `use_wcwidth` | `bool`<br/>(Default: `True`) | Whether to use [wcwidth][wcwidth] instead of `len()` to calculate cell width |
218218

219219
[wcwidth]: https://pypi.org/project/wcwidth/
220220

docs/source/usage.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ Use a preset style
109109
body=[["10", "30", "40", "35"], ["20", "10", "20", "5"]],
110110
style=PresetStyle.plain,
111111
cell_padding=0,
112-
alignments=[Alignment.LEFT] * 4,
112+
alignments=Alignment.LEFT,
113113
)
114114
115115
print(output)

table2ascii/alignment.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
class Alignment(IntEnum):
55
"""Enum for text alignment types within a table cell
66
7-
Example::
7+
A list of alignment types can be used to align each column individually::
88
99
from table2ascii import Alignment, table2ascii
1010
@@ -15,6 +15,8 @@ class Alignment(IntEnum):
1515
["Cheese", "Dairy", "$10.99", "8.2"],
1616
["Apples", "Produce", "$0.99", "10.00"],
1717
],
18+
# Align the first column to the left, the second to the center,
19+
# the third to the right, and the fourth to the decimal point
1820
alignments=[Alignment.LEFT, Alignment.CENTER, Alignment.RIGHT, Alignment.DECIMAL],
1921
)
2022
@@ -28,6 +30,27 @@ class Alignment(IntEnum):
2830
╚════════════════════════════════════════╝
2931
\"\"\"
3032
33+
A single alignment type can be used for all columns::
34+
35+
table2ascii(
36+
header=["First Name", "Last Name", "Age"],
37+
body=[
38+
["John", "Smith", 30],
39+
["Jane", "Doe", 28],
40+
],
41+
# Align all columns to the left
42+
alignments=Alignment.LEFT,
43+
)
44+
45+
\"\"\"
46+
╔══════════════════════════════╗
47+
║ First Name Last Name Age ║
48+
╟──────────────────────────────╢
49+
║ John Smith 30 ║
50+
║ Jane Doe 28 ║
51+
╚══════════════════════════════╝
52+
\"\"\"
53+
3154
.. note::
3255
3356
If the :attr:`DECIMAL` alignment type is used, any cell values that are

table2ascii/options.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class Options:
1919
first_col_heading: bool
2020
last_col_heading: bool
2121
column_widths: Sequence[int | None] | None
22-
alignments: Sequence[Alignment] | None
22+
alignments: Sequence[Alignment] | Alignment | None
2323
cell_padding: int
2424
style: TableStyle
2525
use_wcwidth: bool

table2ascii/table_to_ascii.py

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,16 @@ def __init__(
6767
if not header and not body and not footer:
6868
raise NoHeaderBodyOrFooterError()
6969

70-
self.__alignments = options.alignments or [Alignment.CENTER] * self.__columns
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)
75+
)
7176

7277
# check if alignments specified have a different number of columns
73-
if options.alignments and len(options.alignments) != self.__columns:
74-
raise AlignmentCountMismatchError(options.alignments, self.__columns)
78+
if len(self.__alignments) != self.__columns:
79+
raise AlignmentCountMismatchError(self.__alignments, self.__columns)
7580

7681
# keep track of the number widths and positions of the decimal points for decimal alignment
7782
decimal_widths, decimal_positions = self.__calculate_decimal_widths_and_positions()
@@ -634,16 +639,13 @@ def table2ascii(
634639
first_col_heading: bool = False,
635640
last_col_heading: bool = False,
636641
column_widths: Sequence[int | None] | None = None,
637-
alignments: Sequence[Alignment] | None = None,
642+
alignments: Sequence[Alignment] | Alignment | None = None,
638643
cell_padding: int = 1,
639644
style: TableStyle = PresetStyle.double_thin_compact,
640645
use_wcwidth: bool = True,
641646
) -> str:
642647
"""Convert a 2D Python table to ASCII text
643648
644-
.. versionchanged:: 1.0.0
645-
Added the ``use_wcwidth`` parameter defaulting to :py:obj:`True`.
646-
647649
Args:
648650
header: List of column values in the table's header row. All values should be :class:`str`
649651
or support :class:`str` conversion. If not specified, the table will not have a header row.
@@ -660,8 +662,10 @@ def table2ascii(
660662
is passed instead of a :class:`~collections.abc.Sequence`, all columns will be automatically
661663
sized. Defaults to :py:obj:`None`.
662664
alignments: List of alignments for each column
663-
(ex. ``[Alignment.LEFT, Alignment.CENTER, Alignment.RIGHT]``). If not specified or set to
664-
:py:obj:`None`, all columns will be center-aligned. Defaults to :py:obj:`None`.
665+
(ex. ``[Alignment.LEFT, Alignment.CENTER, Alignment.RIGHT, Alignment.DECIMAL]``)
666+
or a single alignment to apply to all columns (ex. ``Alignment.LEFT``).
667+
If not specified or set to :py:obj:`None`, all columns will be center-aligned.
668+
Defaults to :py:obj:`None`.
665669
cell_padding: The minimum number of spaces to add between the cell content and the column
666670
separator. If set to ``0``, the cell content will be flush against the column separator.
667671
Defaults to ``1``.
@@ -673,6 +677,14 @@ def table2ascii(
673677
zero-width space, etc.), whereas :func:`len` determines the width solely based on the number of
674678
characters in the string. Defaults to :py:obj:`True`.
675679
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`.
687+
676688
Returns:
677689
The generated ASCII table
678690
"""

tests/test_alignments.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,37 @@ def test_decimal_alignment():
120120
"╚═════════════╩═══════╧═════════════╧════╧════╧═════════╝"
121121
)
122122
assert text == expected
123+
124+
125+
def test_single_decimal_alignment():
126+
text = t2a(
127+
header=["1.1.1", "G", "Long Header"],
128+
body=[[100.00001, 2, 3.14], [10.0001, 22.0, 2.718]],
129+
alignments=Alignment.DECIMAL,
130+
)
131+
expected = (
132+
"╔════════════════════════════════╗\n"
133+
"║ 1.1.1 G Long Header ║\n"
134+
"╟────────────────────────────────╢\n"
135+
"║ 100.00001 2 3.14 ║\n"
136+
"║ 10.0001 22.0 2.718 ║\n"
137+
"╚════════════════════════════════╝"
138+
)
139+
assert text == expected
140+
141+
142+
def test_single_left_alignment():
143+
text = t2a(
144+
header=["1.1.1", "G", "Long Header"],
145+
body=[[100.00001, 2, 3.14], [10.0001, 22.0, 2.718]],
146+
alignments=Alignment.LEFT,
147+
)
148+
expected = (
149+
"╔════════════════════════════════╗\n"
150+
"║ 1.1.1 G Long Header ║\n"
151+
"╟────────────────────────────────╢\n"
152+
"║ 100.00001 2 3.14 ║\n"
153+
"║ 10.0001 22.0 2.718 ║\n"
154+
"╚════════════════════════════════╝"
155+
)
156+
assert text == expected

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy