Skip to content

Commit

Permalink
Merge pull request #341 from dn813/master
Browse files Browse the repository at this point in the history
Add table format "colon_grid"
  • Loading branch information
astanin authored Sep 26, 2024
2 parents 23e4fcd + 2855363 commit 3b66d2d
Show file tree
Hide file tree
Showing 2 changed files with 165 additions and 3 deletions.
77 changes: 74 additions & 3 deletions tabulate/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,30 @@ def _pipe_line_with_colons(colwidths, colaligns):
return "|" + "|".join(segments) + "|"


def _grid_segment_with_colons(colwidth, align):
"""Return a segment of a horizontal line with optional colons which indicate
column's alignment in a grid table."""
width = colwidth
if align == "right":
return ("=" * (width - 1)) + ":"
elif align == "center":
return ":" + ("=" * (width - 2)) + ":"
elif align == "left":
return ":" + ("=" * (width - 1))
else:
return "=" * width


def _grid_line_with_colons(colwidths, colaligns):
"""Return a horizontal line with optional colons to indicate column's alignment
in a grid table."""
if not colaligns:
colaligns = [""] * len(colwidths)
segments = [_grid_segment_with_colons(w, a) for a, w in zip(colaligns, colwidths)]
return "+" + "+".join(segments) + "+"



def _mediawiki_row_with_attrs(separator, cell_values, colwidths, colaligns):
alignment = {
"left": "",
Expand Down Expand Up @@ -406,6 +430,16 @@ def escape_empty(val):
padding=1,
with_header_hide=None,
),
"colon_grid": TableFormat(
lineabove=Line("+", "-", "+", "+"),
linebelowheader=_grid_line_with_colons,
linebetweenrows=Line("+", "-", "+", "+"),
linebelow=Line("+", "-", "+", "+"),
headerrow=DataRow("|", "|", "|"),
datarow=DataRow("|", "|", "|"),
padding=1,
with_header_hide=None,
),
"outline": TableFormat(
lineabove=Line("+", "-", "+", "+"),
linebelowheader=Line("+", "=", "+", "+"),
Expand Down Expand Up @@ -699,6 +733,7 @@ def escape_empty(val):
"mixed_grid": "mixed_grid",
"double_grid": "double_grid",
"fancy_grid": "fancy_grid",
"colon_grid": "colon_grid",
"pipe": "pipe",
"orgtbl": "orgtbl",
"jira": "jira",
Expand Down Expand Up @@ -1838,6 +1873,30 @@ def tabulate(
│ eggs │ 451 │
╘═══════════╧═══════════╛
"colon_grid" is similar to "grid" but uses colons only to define
columnwise content alignment, with no whitespace padding:
>>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]],
... ["strings", "numbers"], "colon_grid"))
+-----------+-----------+
| strings | numbers |
+:==========+:==========+
| spam | 41.9999 |
+-----------+-----------+
| eggs | 451 |
+-----------+-----------+
>>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]],
... ["strings", "numbers"], "colon_grid",
... colalign=["right", "left"]))
+-----------+-----------+
| strings | numbers |
+==========:+:==========+
| spam | 41.9999 |
+-----------+-----------+
| eggs | 451 |
+-----------+-----------+
"outline" is the same as the "grid" format but doesn't draw lines between rows:
>>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]],
Expand Down Expand Up @@ -2154,6 +2213,13 @@ def tabulate(
numalign = "decimal" if numalign == _DEFAULT_ALIGN else numalign
stralign = "left" if stralign == _DEFAULT_ALIGN else stralign

# 'colon_grid' uses colons in the line beneath the header to represent a column's
# alignment instead of literally aligning the text differently. Hence,
# left alignment of the data in the text output is enforced.
if tablefmt == "colon_grid":
colglobalalign = "left"
headersglobalalign = "left"

# optimization: look for ANSI control codes once,
# enable smart width functions only if a control code is found
#
Expand Down Expand Up @@ -2222,7 +2288,7 @@ def tabulate(
aligns = [colglobalalign] * len(cols)
else: # default
aligns = [numalign if ct in [int, float] else stralign for ct in coltypes]
# then specific alignements
# then specific alignments
if colalign is not None:
assert isinstance(colalign, Iterable)
if isinstance(colalign, str):
Expand All @@ -2239,9 +2305,14 @@ def tabulate(
minwidths = (
[width_fn(h) + min_padding for h in headers] if headers else [0] * len(cols)
)
aligns_copy = aligns.copy()
# Reset alignments in copy of alignments list to "left" for 'colon_grid' format,
# which enforces left alignment in the text output of the data.
if tablefmt == "colon_grid":
aligns_copy = ["left"] * len(cols)
cols = [
_align_column(c, a, minw, has_invisible, enable_widechars, is_multiline)
for c, a, minw in zip(cols, aligns, minwidths)
for c, a, minw in zip(cols, aligns_copy, minwidths)
]

aligns_headers = None
Expand All @@ -2253,7 +2324,7 @@ def tabulate(
aligns_headers = [headersglobalalign] * len(t_cols)
else: # default
aligns_headers = aligns or [stralign] * len(headers)
# then specific header alignements
# then specific header alignments
if headersalign is not None:
assert isinstance(headersalign, Iterable)
if isinstance(headersalign, str):
Expand Down
91 changes: 91 additions & 0 deletions test/test_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -1418,6 +1418,97 @@ def test_fancy_grid_multiline_row_align():
assert_equal(expected, result)


def test_colon_grid():
"Output: colon_grid with two columns aligned left and center"
expected = "\n".join(
[
"+------+------+",
"| H1 | H2 |",
"+=====:+:====:+",
"| 3 | 4 |",
"+------+------+",
]
)
result = tabulate([[3, 4]], headers=("H1", "H2"), tablefmt="colon_grid", colalign=["right", "center"])
assert_equal(expected, result)


def test_colon_grid_wide_characters():
"Output: colon_grid with wide chars in header"
try:
import wcwidth # noqa
except ImportError:
skip("test_colon_grid_wide_characters is skipped")
headers = list(_test_table_headers)
headers[1] = "配列"
expected = "\n".join(
[
"+-----------+---------+",
"| strings | 配列 |",
"+:==========+========:+",
"| spam | 41.9999 |",
"+-----------+---------+",
"| eggs | 451 |",
"+-----------+---------+",
]
)
result = tabulate(_test_table, headers, tablefmt="colon_grid", colalign=["left", "right"])
assert_equal(expected, result)


def test_colon_grid_headerless():
"Output: colon_grid without headers"
expected = "\n".join(
[
"+------+---------+",
"| spam | 41.9999 |",
"+------+---------+",
"| eggs | 451 |",
"+------+---------+",
]
)
result = tabulate(_test_table, tablefmt="colon_grid")
assert_equal(expected, result)


def test_colon_grid_multiline():
"Output: colon_grid with multiline cells"
table = [["Data\n5", "33\n3"]]
headers = ["H1\n1", "H2\n2"]
expected = "\n".join(
[
"+------+------+",
"| H1 | H2 |",
"| 1 | 2 |",
"+:=====+:=====+",
"| Data | 33 |",
"| 5 | 3 |",
"+------+------+",
]
)
result = tabulate(table, headers, tablefmt="colon_grid")
assert_equal(expected, result)


def test_colon_grid_with_empty_cells():
table = [["A", ""], ["", "B"]]
headers = ["H1", "H2"]
alignments = ["center", "right"]
expected = "\n".join(
[
"+------+------+",
"| H1 | H2 |",
"+:====:+=====:+",
"| A | |",
"+------+------+",
"| | B |",
"+------+------+",
]
)
result = tabulate(table, headers, tablefmt="colon_grid", colalign=alignments)
assert_equal(expected, result)


def test_outline():
"Output: outline with headers"
expected = "\n".join(
Expand Down

0 comments on commit 3b66d2d

Please sign in to comment.