Skip to content

_CustomTextWrap produces lines wider than width after the #399 fix (032dc0d) #478

Open

Description

@RootCauseWeekly

The fix for #399 (032dc0d, on master, not in a release yet) stops the infinite loop, but it also makes some lines wider than the requested width. Before that commit, the same inputs wrapped correctly.

Minimal repro

>>> import tabulate, wcwidth
>>> out = tabulate._CustomTextWrap(width=3).wrap("a 글글")
>>> out, [wcwidth.wcswidth(x) for x in out]
(['a 글', '글'], [4, 2])        # master @ 268615a: first line is 4 columns in a 3-column width

Before 032dc0d (and on PyPI 0.10.0): ['a ', '글', '글'], which fits.

In a table:

>>> print(tabulate.tabulate([["a 글글"]], maxcolwidths=[3], tablefmt="grid"))
+------+
| a 글 |
| 글   |
+------+

Cause

In _handle_long_word:

i = max(i, 2)

This forces at least one character onto the line even when cur_line already has content, so a wide character that doesn't fit in the remaining space gets squeezed onto a non-empty line. Forcing progress is only needed when the line is empty. Taking the character in that case still breaks the loop, and otherwise the chunk can move to the next line as before:

if i == 1 and not cur_line and stripped_chunk:
    i = 2

How I found it

I compared the pre-fix code and master on 6,910 random strings (a mix of ASCII, CJK, emoji, combining marks and ANSI codes, at widths 1/2/3/4/7). The pre-fix code hangs on 1,291 of them. Master fixes all of those hangs, but changes 201 outputs that previously terminated, and all 201 now have a line wider than width. With the guard above, every hang is fixed and none of the previously-terminating outputs change. The full test/ suite passes, including the #399 test.

Happy to open a PR with this change and a regression test if that's useful.

Activity

  1. MbappeWU commented on Oct 24, 2026

    @MbappeWU

    I'll take this one. Plan: change _handle_long_word so the i = max(i, 2) progress force only applies when cur_line is empty (as proposed in the issue), add a regression for the a 글글 / width=3 case, and keep the #399 no-infinite-loop behavior. Will open a PR shortly.

  2. MbappeWU commented on Oct 29, 2026

    @MbappeWU

    I'd like to take this — working on a fix that keeps post-#399 wrapping within width for _CustomTextWrap.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions