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
38 changes: 18 additions & 20 deletions tabulate/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2424,13 +2424,14 @@ def _format_table(fmt, headers, headersaligns, rows, colwidths, colaligns, is_mu
if fmt.linebelowheader and "linebelowheader" not in hidden:
_append_line(lines, padded_widths, colaligns, fmt.linebelowheader)

if padded_rows and fmt.linebetweenrows and "linebetweenrows" not in hidden:
if padded_rows:
# initial rows with a line below
for row, ralign in zip(padded_rows[:-1], rowaligns):
append_row(
lines, row, padded_widths, colaligns, fmt.datarow, rowalign=ralign
)
_append_line(lines, padded_widths, colaligns, fmt.linebetweenrows)
if fmt.linebetweenrows and "linebetweenrows" not in hidden:
_append_line(lines, padded_widths, colaligns, fmt.linebetweenrows)
# the last row without a line below
append_row(
lines,
Expand All @@ -2440,21 +2441,7 @@ def _format_table(fmt, headers, headersaligns, rows, colwidths, colaligns, is_mu
fmt.datarow,
rowalign=rowaligns[-1],
)
else:
separating_line = (
fmt.linebetweenrows
or fmt.linebelowheader
or fmt.linebelow
or fmt.lineabove
or Line("", "", "", "")
)
for row in padded_rows:
# test to see if either the 1st column or the 2nd column (account for showindex) has
# the SEPARATING_LINE flag
if _is_separating_line(row):
_append_line(lines, padded_widths, colaligns, separating_line)
else:
append_row(lines, row, padded_widths, colaligns, fmt.datarow)


if fmt.linebelow and "linebelow" not in hidden:
_append_line(lines, padded_widths, colaligns, fmt.linebelow)
Expand Down Expand Up @@ -2540,10 +2527,21 @@ def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
# take each charcter's width into account
chunk = reversed_chunks[-1]
i = 1
while self._len(chunk[:i]) <= space_left:
# Only count printable characters, so strip_ansi first, index later.
while len(_strip_ansi(chunk)[:i]) <= space_left:
i = i + 1
cur_line.append(chunk[: i - 1])
reversed_chunks[-1] = chunk[i - 1 :]
# Consider escape codes when breaking words up
total_escape_len = 0
last_group = 0
if _ansi_codes.search(chunk) is not None:
for group, _, _, _ in _ansi_codes.findall(chunk):
escape_len = len(group)
if group in chunk[last_group: i + total_escape_len + escape_len - 1]:
total_escape_len += escape_len
found = _ansi_codes.search(chunk[last_group:])
last_group += found.end()
cur_line.append(chunk[: i + total_escape_len - 1])
reversed_chunks[-1] = chunk[i + total_escape_len - 1 :]

# Otherwise, we have to preserve the long word intact. Only add
# it to the current line if there's nothing already there --
Expand Down
1 change: 1 addition & 0 deletions tabulate/version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
version=0.9.0
4 changes: 2 additions & 2 deletions test/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import warnings

def assert_equal(expected, result):
print("Expected:\n%s\n" % expected)
print("Got:\n%s\n" % result)
print("Expected:\n%r\n" % expected)
print("Got:\n%r\n" % result)
assert expected == result


Expand Down
38 changes: 37 additions & 1 deletion test/test_textwrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import datetime

from tabulate import _CustomTextWrap as CTW, tabulate
from tabulate import _CustomTextWrap as CTW, tabulate, _strip_ansi
from textwrap import TextWrapper as OTW

from common import skip, assert_equal
Expand Down Expand Up @@ -158,6 +158,42 @@ def test_wrap_color_line_splillover():
assert_equal(expected, result)


def test_wrap_color_line_longword():
"""TextWrapper: Wrap a line - preserve internal color tags and wrap them to
other lines when required, requires adding the colors tags to other lines as appropriate
and avoiding splitting escape codes."""
data = "This_is_a_\033[31mtest_string_for_testing_TextWrap\033[0m_with_colors"

expected = [
"This_is_a_\033[31mte\033[0m",
"\033[31mst_string_fo\033[0m",
"\033[31mr_testing_Te\033[0m",
"\033[31mxtWrap\033[0m_with_",
"colors",
]
wrapper = CTW(width=12)
result = wrapper.wrap(data)
assert_equal(expected, result)


def test_wrap_color_line_multiple_escapes():
data = "012345(\x1b[32ma\x1b[0mbc\x1b[32mdefghij\x1b[0m)"
expected = [
"012345(\x1b[32ma\x1b[0mbc\x1b[32m\x1b[0m",
"\x1b[32mdefghij\x1b[0m)",
]
wrapper = CTW(width=10)
result = wrapper.wrap(data)
assert_equal(expected, result)

clean_data = _strip_ansi(data)
for width in range(2, len(clean_data)):
wrapper = CTW(width=width)
result = wrapper.wrap(data)
# Comparing after stripping ANSI should be enough to catch broken escape codes
assert_equal(clean_data, _strip_ansi("".join(result)))


def test_wrap_datetime():
"""TextWrapper: Show that datetimes can be wrapped without crashing"""
data = [
Expand Down