astanin / python-tabulate

Pretty-print tabular data in Python, a library and a command-line utility. Repository migrated from bitbucket.org/astanin/python-tabulate.
https://pypi.org/project/tabulate/
MIT License
2.17k stars 164 forks source link

maxcolwidths does not preserve line breaks #190

Closed mliszcz closed 2 years ago

mliszcz commented 2 years ago

maxcolwidths does not work well with multiline cells. Using the latest v0.8.10 I'm observing this behavior:

data = [ ["123456789 bbb\nccc"] ]
print(tabulate.tabulate(data, tablefmt="fancy_grid", maxcolwidths=10))
print(tabulate.tabulate(data, tablefmt="fancy_grid"))
╒═══════════╕
│ 123456789 │
│ bbb ccc   │
╘═══════════╛
╒═══════════════╕
│ 123456789 bbb │
│ ccc           │
╘═══════════════╛

Note that the first one does not have a line break between bbb and ccc. The expected result is:

╒═══════════╕
│ 123456789 │
│ bbb       │
│ ccc       │
╘═══════════╛

This can be fixed by changing the _wrap_text_to_colwidths from:

                wrapper = _CustomTextWrap(width=width)
                wrapped = wrapper.wrap(cell)

to:

                wrapper = _CustomTextWrap(width=width)
                wrapped = ['\n'.join(wrapper.wrap(line)) for line in cell.splitlines() if line.strip() != '']

As described https://stackoverflow.com/a/26538082.

I can send a PR if such change would be accepted.

mliszcz commented 2 years ago

For now, I monkey patched it with this code:

OriginalTextWrap = tabulate._CustomTextWrap
class PatchedTextWrap(OriginalTextWrap):
    def wrap(self, cell):
        return ['\n'.join(super(OriginalTextWrap, self).wrap(line)) for line in cell.splitlines() if line.strip() != '']
tabulate._CustomTextWrap = PatchedTextWrap
harlankoehn commented 2 months ago

I found the monkey patch helpful but also unclear. Here's a more complete version of it.

# patched_tabulate.py
import tabulate as original_tabulate

# This is to  work around an issue where new lines were getting stripped out of column cells when using maxcolwidths option
# Make sure that when you import `tabulate` from this module to use the patched version

# Access the _CustomTextWrap class from the tabulate module
OriginalTextWrap = original_tabulate._CustomTextWrap

# Define the patched version of the _CustomTextWrap class
class PatchedTextWrap(OriginalTextWrap):
    def wrap(self, cell):
        return ['\n'.join(super(OriginalTextWrap, self).wrap(line)) for line in cell.splitlines() if line.strip() != '']

# Apply the monkeypatch
original_tabulate._CustomTextWrap = PatchedTextWrap

# Re-export everything from the original tabulate module
# This makes sure that you can still access all functions and classes as usual.
__all__ = ['tabulate', 'tabulate_formats', 'simple_separated_format']

tabulate = original_tabulate.tabulate
tabulate_formats = original_tabulate.tabulate_formats
simple_separated_format = original_tabulate.simple_separated_format