Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions tabulate/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2386,6 +2386,14 @@ def tabulate(
elif align != "global":
aligns[idx] = align
minwidths = [width_fn(h) + min_padding for h in headers] if headers else [0] * len(cols)
# A header that fits inside an explicit maxcolwidths must not push the column
# past that limit: the extra header padding would be rendered as unwanted
# whitespace (#354). Headers that are wider than the limit keep the previous
# behaviour, so the table stays aligned; `maxheadercolwidths` wraps those.
if headers and maxcolwidths and len(headers) == len(cols):
for i, cap in enumerate(maxcolwidths[: len(minwidths)]):
if cap and width_fn(headers[i]) <= cap:
minwidths[i] = min(minwidths[i], cap)
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.
Expand Down
28 changes: 18 additions & 10 deletions test/test_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,13 @@ def test_plain_maxcolwidth_autowraps_wide_chars():
assert_equal(expected, result)


def test_maxcolwidth_header_does_not_add_padding():
"Output: a header that fits inside maxcolwidths does not widen the column (#354)"
expected = "\n".join(["+-----+", "| foo |", "+=====+", "| bar |", "+-----+"])
result = tabulate([["foo"], ["bar"]], headers="firstrow", tablefmt="grid", maxcolwidths=3)
assert_equal(expected, result)


def test_maxcolwidth_single_value():
"Output: maxcolwidth can be specified as a single number that works for each column"
table = [
Expand All @@ -157,11 +164,11 @@ def test_maxcolwidth_single_value():
]
expected = "\n".join(
[
"hdr fold1 fold2",
"mini this this",
" is is a",
" short bit",
" longer",
"hdr fold1 fold2",
"mini this this",
" is is a",
" short bit",
" longer",
]
)
result = tabulate(table, headers="firstrow", tablefmt="plain", maxcolwidths=6)
Expand All @@ -176,8 +183,8 @@ def test_maxcolwidth_pad_tailing_widths():
]
expected = "\n".join(
[
"hdr fold1 fold2",
"mini this this is a bit longer",
"hdr fold1 fold2",
"mini this this is a bit longer",
" is",
" short",
]
Expand Down Expand Up @@ -3325,13 +3332,14 @@ def test_break_long_words():
table_headers = ["h1", "h2", "h3"]
test_table = [[" foo1", " bar2 ", "foo3"]]

# Table is not wrapped on 3 letters due to long word
# Long words are not broken, so the column keeps the width of the word itself
expected = "h1 h2 h3\n---- ---- ----\nfoo1 bar2 foo3"
result = tabulate(test_table, table_headers, maxcolwidths=3, break_long_words=False)
assert_equal(expected, result)

# Table max width is 3 letters
expected = "h1 h2 h3\n---- ---- ----\nf ba foo\noo1 r2 3"
# Table max width is 3 letters; the header is shorter than the limit, so it
# must not add its 2 spaces of padding on top of the limit (#354)
expected = "h1 h2 h3\n--- --- ---\nf ba foo\noo1 r2 3"
result = tabulate(test_table, table_headers, maxcolwidths=3, break_long_words=True)
assert_equal(expected, result)

Expand Down
80 changes: 40 additions & 40 deletions test/test_textwrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,19 +240,19 @@ def test_wrap_datetime():
result = tabulate(data, headers=headers, tablefmt="grid", maxcolwidths=[7, 5])

expected = [
"+---------+--------+",
"| Title | When |",
"+=========+========+",
"| First | 2020- |",
"| Entry | 01-01 |",
"| | 05:06 |",
"| | :07 |",
"+---------+--------+",
"| Second | 2021- |",
"| Entry | 02-02 |",
"| | 00:00 |",
"| | :00 |",
"+---------+--------+",
"+---------+-------+",
"| Title | When |",
"+=========+=======+",
"| First | 2020- |",
"| Entry | 01-01 |",
"| | 05:06 |",
"| | :07 |",
"+---------+-------+",
"| Second | 2021- |",
"| Entry | 02-02 |",
"| | 00:00 |",
"| | :00 |",
"+---------+-------+",
]
expected = "\n".join(expected)
assert_equal(expected, result)
Expand All @@ -265,15 +265,15 @@ def test_wrap_none_value():
result = tabulate(data, headers=headers, tablefmt="grid", maxcolwidths=[7, 5])

expected = [
"+---------+---------+",
"| Title | Value |",
"+=========+=========+",
"| First | |",
"| Entry | |",
"+---------+---------+",
"| Second | |",
"| Entry | |",
"+---------+---------+",
"+---------+-------+",
"| Title | Value |",
"+=========+=======+",
"| First | |",
"| Entry | |",
"+---------+-------+",
"| Second | |",
"| Entry | |",
"+---------+-------+",
]
expected = "\n".join(expected)
assert_equal(expected, result)
Expand All @@ -288,15 +288,15 @@ def test_wrap_none_value_with_missingval():
)

expected = [
"+---------+---------+",
"| Title | Value |",
"+=========+=========+",
"| First | ??? |",
"| Entry | |",
"+---------+---------+",
"| Second | ??? |",
"| Entry | |",
"+---------+---------+",
"+---------+-------+",
"| Title | Value |",
"+=========+=======+",
"| First | ??? |",
"| Entry | |",
"+---------+-------+",
"| Second | ??? |",
"| Entry | |",
"+---------+-------+",
]
expected = "\n".join(expected)
assert_equal(expected, result)
Expand All @@ -312,15 +312,15 @@ def test_wrap_optional_bool_strs():
result = tabulate(data, headers=headers, tablefmt="grid", maxcolwidths=[7, 5])

expected = [
"+---------+--------+",
"| Title | When |",
"+=========+========+",
"| First | True |",
"| Entry | |",
"+---------+--------+",
"| Second | |",
"| Entry | |",
"+---------+--------+",
"+---------+-------+",
"| Title | When |",
"+=========+=======+",
"| First | True |",
"| Entry | |",
"+---------+-------+",
"| Second | |",
"| Entry | |",
"+---------+-------+",
]
expected = "\n".join(expected)
assert_equal(expected, result)
Expand Down
51 changes: 51 additions & 0 deletions test_354_acceptance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env python3
"""python-tabulate #354 的验收(可执行断言,不是形容词)。

用法:在 python-tabulate 的 clone 根目录跑
PYTHONPATH=. python3 test_354_acceptance.py
全部 PASS 退出码 0;有 FAIL 退出码 1。改前应当看见 A 是红的。
"""
import sys

from tabulate import _wrap_text_to_colwidths, tabulate

CELL = "The files were concatenated and archived for posterity."


def cell_width(header, w):
"""渲染出来那一格的内容宽(去掉 fancy_grid 两侧各一个空格)。"""
t = tabulate([[CELL]], headers=[header], tablefmt="fancy_grid", maxcolwidths=[w])
lines = [l for l in t.splitlines() if l.startswith("\u2502")]
cell = lines[1].split("\u2502")[1]
return len(cell) - 2


def check(name, got, want):
ok = got == want
print(("PASS " if ok else "FAIL ") + "{}: 实测={} 期望={}".format(name, got, want))
return ok


def main():
results = []

# A:表头 8 字、上限 8 → 内容宽必须是 8。
# 改前实测 10(单元格宽 12),这就是 #354 的偏差本身。
results.append(check("A 表头 Header#3 + maxcolwidths=8", cell_width("Header#3", 8), 8))

# B:表头比上限短时本来就对。用来证明缺陷跟表头长度有关,不是折行。
results.append(check("B 表头 h1 + maxcolwidths=8", cell_width("h1", 8), 8))

# C:数据格的折行不许被改动(上限就是上限,最长行必须等于 8)。
# 它是护栏:谁去“修” TextWrapper,这条就会红。
wrapped = _wrap_text_to_colwidths([[CELL]], [8])[0][0]
longest = max(len(line) for line in wrapped.split("\n"))
results.append(check("C 折行最长行 <= 上限", longest, 8))

print("---")
print("结论:{}".format("全部通过" if all(results) else "有 FAIL,别提交补丁"))
return 0 if all(results) else 1


if __name__ == "__main__":
sys.exit(main())
Loading