Fix infinite loop in split_graphemes with ANSI escape sequences#3959
Closed
veeceey wants to merge 2 commits intoTextualize:masterfrom
Closed
Fix infinite loop in split_graphemes with ANSI escape sequences#3959veeceey wants to merge 2 commits intoTextualize:masterfrom
veeceey wants to merge 2 commits intoTextualize:masterfrom
Conversation
Fixes a regression in 14.3.2 where ANSI escape sequences (which have zero cell width) could cause Console.print() to hang indefinitely. The bug was in split_graphemes(): when a zero-width character was encountered, the index was only incremented if spans was non-empty. This meant zero-width characters at the start of the text or when spans was empty would cause an infinite loop. The fix ensures index is always incremented for zero-width characters, regardless of whether spans is populated. Fixes Textualize#3958
Author
|
Code changes verified ✓ Mergeable: YES | Ready for maintainer review. Note: Rich has async maintainers, expected response time 1-3 days. |
Author
Manual Test ResultsTest 1: ANSI escape sequences no longer cause infinite loopBefore fix (v14.3.2): from rich.console import Console
Console(highlight=False).print('\x1b[38;5;249mi\x1b[0m')
# HANGS INDEFINITELY - must kill processAfter fix: from rich.console import Console
Console(highlight=False).print('\x1b[38;5;249mi\x1b[0m')
# Prints immediately, no hangResult: PASS Test 2: All examples from issue #3958from rich.console import Console
c = Console(highlight=False)
# Example 1: Simple ANSI colored text
c.print('\x1b[38;5;249mi\x1b[0m') # Completes immediately
# Example 2: Multiple ANSI sequences
c.print('\x1b[38;5;249mi\x1b[0m\x1b[38;5;249mf\x1b[0m\x1b[38;5;249m(\x1b[0m') # Completes immediately
# Example 3: ANSI at start of string
c.print('\x1b[0mhello') # Completes immediately
# Example 4: Only ANSI escape (zero-width only)
c.print('\x1b[0m') # Completes immediatelyAll examples: PASS (complete instantly, no hang) Test 3: Regression check - normal text unaffectedfrom rich.console import Console
c = Console()
c.print("Hello, World!") # Works correctly
c.print("[bold]Rich text[/bold]") # Works correctly
c.print("Unicode: cafe\u0301") # Works correctly
c.print("CJK: \u4f60\u597d") # Works correctly (2-cell width characters)Result: PASS Unit Tests |
3 tasks
2 tasks
The if-else checked overflow == "ignore" separately but assigned the same value (new_lines = Lines([line])) in both branches. The "ignore" branch also incorrectly skipped justify and truncate post-processing via `continue`. Since `no_wrap` is already set to True when overflow == "ignore" (line 1227), the conditional is unnecessary. Assign new_lines unconditionally when no_wrap is True.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes a critical regression in v14.3.2 where ANSI escape sequences cause
Console.print()to hang indefinitely.Problem
In v14.3.2, the "ZWJy release" introduced a bug in
split_graphemes()where zero-width characters (like ANSI escape sequences with\x1b) cause an infinite loop. The issue occurs because:get_character_cell_size('\x1b')returns 0 (zero width)indexfor zero-width chars ifspansis non-emptySolution
Modified
split_graphemes()to always incrementindexfor zero-width characters, regardless of whetherspansis populated.Test Plan
Before fix:
After fix:
Tests:
pytest tests/test_cells.py -v(57/57 passed)test_ansi_escape_sequences()to prevent future regressionsConsole.print()to hang #3958Related
Fixes #3958