diff --git a/Lib/test/test_textwrap.py b/Lib/test/test_textwrap.py index aca1f427656bb5..ea8b3bfcc65b08 100644 --- a/Lib/test/test_textwrap.py +++ b/Lib/test/test_textwrap.py @@ -615,6 +615,18 @@ def test_break_long(self): 'word,', 'anyways?']) + def test_indent_wider_than_width(self): + # These used to loop forever or to add an extra last line when the + # indent was not narrower than the width. + self.check_wrap(' x', 1, [' x'], initial_indent=' ') + self.check_wrap(' ', 5, [], initial_indent=' ') + self.check_wrap('abc', 2, ['ab', ' c'], + subsequent_indent=' ', drop_whitespace=False) + self.check_wrap('x', 1, [' x'], + initial_indent=' ', drop_whitespace=False) + self.check_wrap('abcdef', 3, ['abc', ' d', ' e', ' f'], + subsequent_indent=' ', drop_whitespace=False) + def test_nobreak_long(self): # Test with break_long_words disabled self.wrapper.break_long_words = 0 diff --git a/Lib/textwrap.py b/Lib/textwrap.py index 2f213e34c2c329..b04b6301914ae0 100644 --- a/Lib/textwrap.py +++ b/Lib/textwrap.py @@ -221,7 +221,12 @@ def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width): if hyphen > 0 and any(c != '-' for c in chunk[:hyphen]): end = hyphen + 1 cur_line.append(chunk[:end]) - reversed_chunks[-1] = chunk[end:] + if end < len(chunk): + reversed_chunks[-1] = chunk[end:] + else: + # The whole chunk fit, which happens only when the indent is + # not narrower than the width. Don't leave an empty chunk. + reversed_chunks.pop() # Otherwise, we have to preserve the long word intact. Only add # it to the current line if there's nothing already there -- diff --git a/Misc/NEWS.d/next/Library/2026-10-07-12-21-15.gh-issue-158979.TkrgLs.rst b/Misc/NEWS.d/next/Library/2026-10-07-12-21-15.gh-issue-158979.TkrgLs.rst new file mode 100644 index 00000000000000..9d82d494b42033 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-10-07-12-21-15.gh-issue-158979.TkrgLs.rst @@ -0,0 +1,4 @@ +Fix an infinite loop in :func:`textwrap.wrap` and :func:`textwrap.fill` when +the indent is wider than *width* and the text starts with whitespace, or +*drop_whitespace* is false. They also no longer add an empty or indent-only +last line in some cases where the indent is not narrower than *width*.