From a8c7236f70a7d9e8c8641da70838996eefb7e5af Mon Sep 17 00:00:00 2001 From: Daniel Hollas Date: Sun, 2 Jun 2024 12:58:18 +0100 Subject: [PATCH 1/5] Add tests --- Lib/test/test_pyrepl/test_reader.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Lib/test/test_pyrepl/test_reader.py b/Lib/test/test_pyrepl/test_reader.py index c9b03d5e711539..be8dd3d4097e72 100644 --- a/Lib/test/test_pyrepl/test_reader.py +++ b/Lib/test/test_pyrepl/test_reader.py @@ -4,6 +4,7 @@ from .support import handle_all_events, handle_events_narrow_console, code_to_events, prepare_reader from _pyrepl.console import Event +from _pyrepl.reader import Reader class TestReader(TestCase): @@ -176,3 +177,19 @@ def test_newline_within_block_trailing_whitespace(self): ) self.assert_screen_equals(reader, expected) self.assertTrue(reader.finished) + + def test_prompt_length(self): + # Handles simple ASCII prompt + ps1 = ">>> " + _, l = Reader.process_prompt(ps1) + self.assertEqual(l, 4) + + # Handles ANSI escape sequences + ps1 = "\001\033[0;32m\002>>> \001\033[0m\002" + _, l = Reader.process_prompt(ps1) + self.assertEqual(l, 4) + + # Handles wide characters in prompt + ps1 = "樂>> " + _, l = Reader.process_prompt(ps1) + self.assertEqual(l, 5) From 1e7829e9812181a711b96059bddc294e088c4d58 Mon Sep 17 00:00:00 2001 From: Daniel Hollas Date: Sun, 2 Jun 2024 13:05:34 +0100 Subject: [PATCH 2/5] Fix prompt width with ANSI escape chars and wide chars --- Lib/_pyrepl/reader.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Lib/_pyrepl/reader.py b/Lib/_pyrepl/reader.py index 5401ae7b0ae32d..f2e68ef6f3ee66 100644 --- a/Lib/_pyrepl/reader.py +++ b/Lib/_pyrepl/reader.py @@ -28,7 +28,7 @@ from . import commands, console, input -from .utils import ANSI_ESCAPE_SEQUENCE, wlen +from .utils import ANSI_ESCAPE_SEQUENCE, wlen, str_width from .trace import trace @@ -339,7 +339,8 @@ def calc_complete_screen(self) -> list[str]: screeninfo.append((0, [])) return screen - def process_prompt(self, prompt: str) -> tuple[str, int]: + @staticmethod + def process_prompt(prompt: str) -> tuple[str, int]: """Process the prompt. This means calculate the length of the prompt. The character \x01 @@ -351,6 +352,11 @@ def process_prompt(self, prompt: str) -> tuple[str, int]: # sequences if they were not explicitly within \x01...\x02. # They are CSI (or ANSI) sequences ( ESC [ ... LETTER ) + # wlen from utils already excludes ANSI_ESCAPE_SEQUENCE chars, + # which breaks the logic below so we redefine it here. + def wlen(s: str) -> int: + return sum(str_width(i) for i in s) + out_prompt = "" l = wlen(prompt) pos = 0 From ae18b704830d742daaf9fb45c27f5879f6fc79a3 Mon Sep 17 00:00:00 2001 From: Daniel Hollas Date: Sun, 2 Jun 2024 13:12:32 +0100 Subject: [PATCH 3/5] More tests --- Lib/test/test_pyrepl/test_reader.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_pyrepl/test_reader.py b/Lib/test/test_pyrepl/test_reader.py index be8dd3d4097e72..2adf1101eac4da 100644 --- a/Lib/test/test_pyrepl/test_reader.py +++ b/Lib/test/test_pyrepl/test_reader.py @@ -181,15 +181,24 @@ def test_newline_within_block_trailing_whitespace(self): def test_prompt_length(self): # Handles simple ASCII prompt ps1 = ">>> " - _, l = Reader.process_prompt(ps1) + prompt, l = Reader.process_prompt(ps1) + self.assertEqual(prompt, ps1) self.assertEqual(l, 4) # Handles ANSI escape sequences ps1 = "\001\033[0;32m\002>>> \001\033[0m\002" - _, l = Reader.process_prompt(ps1) + prompt, l = Reader.process_prompt(ps1) + self.assertEqual(prompt, "\033[0;32m>>> \033[0m") self.assertEqual(l, 4) # Handles wide characters in prompt ps1 = "樂>> " - _, l = Reader.process_prompt(ps1) + prompt, l = Reader.process_prompt(ps1) + self.assertEqual(prompt, ps1) + self.assertEqual(l, 5) + + # Handles wide characters AND ANSI sequences together + ps1 = "\001\033[0;32m\002樂>\001\033[0m\002> " + prompt, l = Reader.process_prompt(ps1) + self.assertEqual(prompt, "\033[0;32m樂>\033[0m> ") self.assertEqual(l, 5) From 00f12e3686de9466cc74f5f314bb01c1aad55ef1 Mon Sep 17 00:00:00 2001 From: Daniel Hollas Date: Sun, 2 Jun 2024 15:09:37 +0100 Subject: [PATCH 4/5] Blurb --- .../next/Library/2024-06-02-15-09-17.gh-issue-118835.KUAuz6.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-06-02-15-09-17.gh-issue-118835.KUAuz6.rst diff --git a/Misc/NEWS.d/next/Library/2024-06-02-15-09-17.gh-issue-118835.KUAuz6.rst b/Misc/NEWS.d/next/Library/2024-06-02-15-09-17.gh-issue-118835.KUAuz6.rst new file mode 100644 index 00000000000000..cf6050092e9cf9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-06-02-15-09-17.gh-issue-118835.KUAuz6.rst @@ -0,0 +1,2 @@ +Fix _pyrepl crash when using custom prompt with ANSI escape codes. Patch by +Daniel Hollas. From 1113535aff5e3063c0a055709d71048e66158241 Mon Sep 17 00:00:00 2001 From: Daniel Hollas Date: Sun, 2 Jun 2024 16:12:52 +0100 Subject: [PATCH 5/5] Add one more test --- Lib/test/test_pyrepl/test_reader.py | 6 ++++++ .../Library/2024-06-02-15-09-17.gh-issue-118835.KUAuz6.rst | 3 +-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_pyrepl/test_reader.py b/Lib/test/test_pyrepl/test_reader.py index 2adf1101eac4da..9fb956b655594f 100644 --- a/Lib/test/test_pyrepl/test_reader.py +++ b/Lib/test/test_pyrepl/test_reader.py @@ -186,6 +186,12 @@ def test_prompt_length(self): self.assertEqual(l, 4) # Handles ANSI escape sequences + ps1 = "\033[0;32m>>> \033[0m" + prompt, l = Reader.process_prompt(ps1) + self.assertEqual(prompt, "\033[0;32m>>> \033[0m") + self.assertEqual(l, 4) + + # Handles ANSI escape sequences bracketed in \001 .. \002 ps1 = "\001\033[0;32m\002>>> \001\033[0m\002" prompt, l = Reader.process_prompt(ps1) self.assertEqual(prompt, "\033[0;32m>>> \033[0m") diff --git a/Misc/NEWS.d/next/Library/2024-06-02-15-09-17.gh-issue-118835.KUAuz6.rst b/Misc/NEWS.d/next/Library/2024-06-02-15-09-17.gh-issue-118835.KUAuz6.rst index cf6050092e9cf9..ec9ca20a487d76 100644 --- a/Misc/NEWS.d/next/Library/2024-06-02-15-09-17.gh-issue-118835.KUAuz6.rst +++ b/Misc/NEWS.d/next/Library/2024-06-02-15-09-17.gh-issue-118835.KUAuz6.rst @@ -1,2 +1 @@ -Fix _pyrepl crash when using custom prompt with ANSI escape codes. Patch by -Daniel Hollas. +Fix _pyrepl crash when using custom prompt with ANSI escape codes.