Skip to content

Commit 6d29538

Browse files
feat: Add cell_padding configurable option (#52)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent a5b7272 commit 6d29538

File tree

5 files changed

+137
-9
lines changed

5 files changed

+137
-9
lines changed

README.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ print(output)
9191
### Use a preset style
9292

9393
```py
94-
from table2ascii import table2ascii, PresetStyle
94+
from table2ascii import table2ascii, Alignment, PresetStyle
9595

9696
output = table2ascii(
9797
header=["First", "Second", "Third", "Fourth"],
@@ -111,6 +111,22 @@ print(output)
111111
| 20 | 10 | 20 | 5 |
112112
+----------+----------+----------+----------+
113113
"""
114+
115+
output = table2ascii(
116+
header=["First", "Second", "Third", "Fourth"],
117+
body=[["10", "30", "40", "35"], ["20", "10", "20", "5"]],
118+
style=PresetStyle.plain,
119+
cell_padding=0,
120+
alignments=[Alignment.LEFT] * 4,
121+
)
122+
123+
print(output)
124+
125+
"""
126+
First Second Third Fourth
127+
10 30 40 35
128+
20 10 20 5
129+
"""
114130
```
115131

116132
### Define a custom style
@@ -159,6 +175,7 @@ All parameters are optional.
159175
| `style` | `TableStyle` | `double_thin_compact` | Table style to use for the table |
160176
| `first_col_heading` | `bool` | `False` | Whether to add a heading column seperator after the first column |
161177
| `last_col_heading` | `bool` | `False` | Whether to add a heading column seperator before the last column |
178+
| `cell_padding` | `int` | `1` | The minimum number of spaces to add between the cell content and the cell border. |
162179

163180
See the [API Reference](https://table2ascii.readthedocs.io/en/latest/api.html) for more info.
164181

docs/source/usage.rst

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ Use a preset style
7979

8080
.. code:: py
8181
82-
from table2ascii import table2ascii, PresetStyle
82+
from table2ascii import table2ascii, Alignment, PresetStyle
8383
8484
output = table2ascii(
8585
header=["First", "Second", "Third", "Fourth"],
@@ -100,6 +100,22 @@ Use a preset style
100100
+----------+----------+----------+----------+
101101
"""
102102
103+
output = table2ascii(
104+
header=["First", "Second", "Third", "Fourth"],
105+
body=[["10", "30", "40", "35"], ["20", "10", "20", "5"]],
106+
style=PresetStyle.plain,
107+
cell_padding=0,
108+
alignments=[Alignment.LEFT] * 4,
109+
)
110+
111+
print(output)
112+
113+
"""
114+
First Second Third Fourth
115+
10 30 40 35
116+
20 10 20 5
117+
"""
118+
103119
Define a custom style
104120
~~~~~~~~~~~~~~~~~~~~~
105121

table2ascii/options.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ class Options:
1313
last_col_heading: bool
1414
column_widths: Optional[List[Optional[int]]]
1515
alignments: Optional[List[Alignment]]
16+
cell_padding: int
1617
style: TableStyle

table2ascii/table_to_ascii.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ def __init__(
3434
self.__style = options.style
3535
self.__first_col_heading = options.first_col_heading
3636
self.__last_col_heading = options.last_col_heading
37+
self.__cell_padding = options.cell_padding
3738

3839
# calculate number of columns
3940
self.__columns = self.__count_columns()
@@ -71,6 +72,10 @@ def __init__(
7172
if options.alignments and len(options.alignments) != self.__columns:
7273
raise ValueError("Length of `alignments` list must equal the number of columns")
7374

75+
# check if the cell padding is valid
76+
if self.__cell_padding < 0:
77+
raise ValueError("Cell padding must be greater than or equal to 0")
78+
7479
def __count_columns(self) -> int:
7580
"""
7681
Get the number of columns in the table based on the
@@ -108,8 +113,8 @@ def widest_line(value: SupportsStr) -> int:
108113
header_size = widest_line(self.__header[i]) if self.__header else 0
109114
body_size = max(widest_line(row[i]) for row in self.__body) if self.__body else 0
110115
footer_size = widest_line(self.__footer[i]) if self.__footer else 0
111-
# get the max and add 2 for padding each side with a space
112-
column_widths.append(max(header_size, body_size, footer_size) + 2)
116+
# get the max and add 2 for padding each side with a space depending on cell padding
117+
column_widths.append(max(header_size, body_size, footer_size) + self.__cell_padding * 2)
113118
return column_widths
114119

115120
def __pad(self, cell_value: SupportsStr, width: int, alignment: Alignment) -> str:
@@ -125,17 +130,19 @@ def __pad(self, cell_value: SupportsStr, width: int, alignment: Alignment) -> st
125130
The padded text
126131
"""
127132
text = str(cell_value)
133+
padding = " " * self.__cell_padding
134+
padded_text = f"{padding}{text}{padding}"
128135
if alignment == Alignment.LEFT:
129136
# pad with spaces on the end
130-
return f" {text} " + (" " * (width - len(text) - 2))
137+
return padded_text + (" " * (width - len(padded_text)))
131138
if alignment == Alignment.CENTER:
132139
# pad with spaces, half on each side
133-
before = " " * floor((width - len(text) - 2) / 2)
134-
after = " " * ceil((width - len(text) - 2) / 2)
135-
return before + f" {text} " + after
140+
before = " " * floor((width - len(padded_text)) / 2)
141+
after = " " * ceil((width - len(padded_text)) / 2)
142+
return before + padded_text + after
136143
if alignment == Alignment.RIGHT:
137144
# pad with spaces at the beginning
138-
return (" " * (width - len(text) - 2)) + f" {text} "
145+
return (" " * (width - len(padded_text))) + padded_text
139146
raise ValueError(f"The value '{alignment}' is not valid for alignment.")
140147

141148
def __row_to_ascii(
@@ -318,6 +325,7 @@ def table2ascii(
318325
last_col_heading: bool = False,
319326
column_widths: Optional[List[Optional[int]]] = None,
320327
alignments: Optional[List[Alignment]] = None,
328+
cell_padding: int = 1,
321329
style: TableStyle = PresetStyle.double_thin_compact,
322330
) -> str:
323331
"""
@@ -341,6 +349,9 @@ def table2ascii(
341349
alignments: List of alignments for each column
342350
(ex. ``[Alignment.LEFT, Alignment.CENTER, Alignment.RIGHT]``). If not specified or set to
343351
:py:obj:`None`, all columns will be center-aligned. Defaults to :py:obj:`None`.
352+
cell_padding: The minimum number of spaces to add between the cell content and the column
353+
separator. If set to ``0``, the cell content will be flush against the column separator.
354+
Defaults to ``1``.
344355
style: Table style to use for styling (preset styles can be imported).
345356
Defaults to :ref:`PresetStyle.double_thin_compact <PresetStyle.double_thin_compact>`.
346357
@@ -356,6 +367,7 @@ def table2ascii(
356367
last_col_heading=last_col_heading,
357368
column_widths=column_widths,
358369
alignments=alignments,
370+
cell_padding=cell_padding,
359371
style=style,
360372
),
361373
).to_ascii()

tests/test_cell_padding.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import pytest
2+
3+
from table2ascii import Alignment, table2ascii as t2a
4+
5+
6+
def test_without_cell_padding():
7+
text = t2a(
8+
header=["#", "G", "H", "R", "S"],
9+
body=[[1, 2, 3, 4, 5]],
10+
footer=["A", "B", 1, 2, 3],
11+
first_col_heading=True,
12+
cell_padding=0,
13+
)
14+
expected = (
15+
"╔═╦═══════╗\n"
16+
"║#║G H R S║\n"
17+
"╟─╫───────╢\n"
18+
"║1║2 3 4 5║\n"
19+
"╟─╫───────╢\n"
20+
"║A║B 1 2 3║\n"
21+
"╚═╩═══════╝"
22+
)
23+
assert text == expected
24+
25+
26+
def test_column_width_and_alignment_without_cell_padding():
27+
text = t2a(
28+
header=["#", "G", "H", "R", "S"],
29+
body=[[1, 2, 3, 4, 5]],
30+
footer=["A", "B", 1, 2, 3],
31+
column_widths=[4, 8, 5, 4, 5],
32+
alignments=[
33+
Alignment.LEFT,
34+
Alignment.CENTER,
35+
Alignment.RIGHT,
36+
Alignment.LEFT,
37+
Alignment.RIGHT,
38+
],
39+
first_col_heading=True,
40+
cell_padding=0,
41+
)
42+
expected = (
43+
"╔════╦═════════════════════════╗\n"
44+
"║# ║ G H R S║\n"
45+
"╟────╫─────────────────────────╢\n"
46+
"║1 ║ 2 3 4 5║\n"
47+
"╟────╫─────────────────────────╢\n"
48+
"║A ║ B 1 2 3║\n"
49+
"╚════╩═════════════════════════╝"
50+
)
51+
assert text == expected
52+
53+
54+
def test_cell_padding_more_than_one():
55+
text = t2a(
56+
header=["#", "G", "H", "R", "S"],
57+
body=[[1, 2, 3, 4, 5]],
58+
footer=["A", "B", 1, 2, 3],
59+
first_col_heading=True,
60+
cell_padding=2,
61+
)
62+
expected = (
63+
"╔═════╦═══════════════════════╗\n"
64+
"║ # ║ G H R S ║\n"
65+
"╟─────╫───────────────────────╢\n"
66+
"║ 1 ║ 2 3 4 5 ║\n"
67+
"╟─────╫───────────────────────╢\n"
68+
"║ A ║ B 1 2 3 ║\n"
69+
"╚═════╩═══════════════════════╝"
70+
)
71+
assert text == expected
72+
73+
74+
def test_negative_cell_padding():
75+
with pytest.raises(ValueError):
76+
t2a(
77+
header=["#", "G", "H", "R", "S"],
78+
body=[[1, 2, 3, 4, 5]],
79+
footer=["A", "B", 1, 2, 3],
80+
first_col_heading=True,
81+
cell_padding=-1,
82+
)

0 commit comments

Comments
 (0)