You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
textwrap truncation discards a broken long word (and, for shorten(), the whole text) instead of breaking it further #160633
With max_lines set (or with shorten(), which uses max_lines=1), when the last line consists of a single full-width chunk produced by _handle_long_word() — i.e. a piece of a long word that break_long_words=True (the default) broke across lines — the truncation loop in _wrap_chunks() drops the whole chunk instead of breaking it further to make room for the placeholder. All remaining text can be silently replaced by the placeholder alone:
The docs for shorten() promise that "enough words are dropped from the end so that the remaining words plus the placeholder fit within width", and the docstring says "as many words as possible are joined". When the text is a single long word, the current implementation cannot keep any part of it at all — the output carries no information about the text. Since break_long_words=True is the default, the wrapper already breaks this word on earlier lines; refusing to break it on the final truncation line discards content that would fit.
This only affects lines consisting of a single full-width chunk. All other documented truncation behavior (dropping whole words from the end, backtracking the placeholder onto the previous line, and the eager "placeholder too large for max width" check) stays exactly the same.
Analysis
In TextWrapper._wrap_chunks() (Lib/textwrap.py), the else branch that places the placeholder drops chunks one at a time until the placeholder fits:
whilecur_line:
if (cur_line[-1].strip() andcur_len+len(self.placeholder) <=width):
cur_line.append(self.placeholder)
lines.append(indent+''.join(cur_line))
breakcur_len-=len(cur_line[-1])
delcur_line[-1]
For a line whose only chunk is a full-width piece of a broken long word, no chunk ever fits with the placeholder, so the loop empties cur_line and the bare placeholder is emitted. Breaking the chunk instead (to width - len(self.placeholder) characters) preserves part of the word and is consistent with break_long_words=True and with how _handle_long_word() already broke the word on previous lines.
The affected expectations were frozen when the feature was implemented in gh-62925 (bpo-18585, 2015) and were never discussed separately.
Activity