Skip to content

Commit 6264ad9

Browse files
edvilmeblurb-it[bot]pablogsalCopilot
committed
gh-152068: Reset PyREPL Colors on prompt finish (#152108)
* Use ANSI Escape Codes from Colorize * Print ANSI Color reset on finish and restore * πŸ“œπŸ€– Added by blurb_it. * Update imports * Update Misc/NEWS.d/next/macOS/2026-06-24-18-00-39.gh-issue-152068.ThsmJU.rst Co-authored-by: Pablo Galindo Salgado <Pablogsal@gmail.com> * Remove news entry * πŸ“œπŸ€– Added by blurb_it. * gh-152068: Test ANSI reset is emitted on console finish() and restore() Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Pablo Galindo Salgado <Pablogsal@gmail.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent f838462 commit 6264ad9

5 files changed

Lines changed: 55 additions & 1 deletion

File tree

β€ŽLib/_pyrepl/unix_console.pyβ€Ž

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
import platform
3434
from fcntl import ioctl
3535

36+
from _colorize import ANSIColors
37+
3638
from . import terminfo
3739
from .console import Console, Event
3840
from .fancy_termios import tcgetattr, tcsetattr, TermState
@@ -382,6 +384,8 @@ def restore(self):
382384
"""
383385
Restore the console to the default state
384386
"""
387+
trace("unix.restore")
388+
self.__write(ANSIColors.RESET)
385389
self.__disable_bracketed_paste()
386390
self.__maybe_write_code(self._rmkx)
387391
self.flushoutput()
@@ -518,6 +522,7 @@ def finish(self):
518522
while y >= 0 and not self.screen[y]:
519523
y -= 1
520524
self.__move(0, min(y, self.height + self.__offset - 1))
525+
self.__write(ANSIColors.RESET)
521526
self.__write("\n\r")
522527
self.flushoutput()
523528

β€ŽLib/_pyrepl/windows_console.pyβ€Ž

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@
3737
SHORT,
3838
)
3939
from ctypes import Structure, POINTER, Union
40+
from typing import TYPE_CHECKING
41+
42+
from _colorize import ANSIColors
43+
4044
from .console import Event, Console
4145
from .trace import trace
4246
from .utils import wlen
@@ -363,6 +367,7 @@ def prepare(self) -> None:
363367

364368
def restore(self) -> None:
365369
if self.__vt_support:
370+
self.__write(ANSIColors.RESET)
366371
# Recover to original mode before running REPL
367372
self._disable_bracketed_paste()
368373
SetConsoleMode(InHandle, self.__original_input_mode)
@@ -519,6 +524,7 @@ def finish(self) -> None:
519524
while y >= 0 and not self.screen[y]:
520525
y -= 1
521526
self._move_relative(0, min(y, self.height + self.__offset - 1))
527+
self.__write(ANSIColors.RESET)
522528
self.__write("\r\n")
523529

524530
def flushoutput(self) -> None:

β€ŽLib/test/test_pyrepl/test_unix_console.pyβ€Ž

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
import threading
77
import unittest
88
from functools import partial
9-
from test.support import os_helper, force_not_colorized_test_class
9+
from _colorize import ANSIColors
10+
from test.support import force_color, os_helper, force_not_colorized_test_class
1011
from test.support import threading_helper
1112

1213
from unittest import TestCase
@@ -107,6 +108,24 @@ def test_no_newline(self, _os_write):
107108
self.assertNotIn(call(ANY, b'\n'), _os_write.mock_calls)
108109
con.restore()
109110

111+
def test_reset_on_finish(self, _os_write):
112+
# gh-152068: finish() must emit the ANSI reset sequence so any
113+
# active color does not leak past the prompt.
114+
code = "1"
115+
events = code_to_events(code)
116+
_, con = handle_events_unix_console(events)
117+
con.finish()
118+
_os_write.assert_any_call(ANY, ANSIColors.RESET.encode(con.encoding))
119+
con.restore()
120+
121+
def test_reset_on_restore(self, _os_write):
122+
# gh-152068: restore() must emit the ANSI reset sequence.
123+
code = "1"
124+
events = code_to_events(code)
125+
_, con = handle_events_unix_console(events)
126+
con.restore()
127+
_os_write.assert_any_call(ANY, ANSIColors.RESET.encode(con.encoding))
128+
110129
def test_newline(self, _os_write):
111130
code = "\n"
112131
events = code_to_events(code)

β€ŽLib/test/test_pyrepl/test_windows_console.pyβ€Ž

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77

88
import itertools
9+
from _colorize import ANSIColors
910
from functools import partial
1011
from test.support import force_not_colorized_test_class
1112
from typing import Iterable
@@ -366,6 +367,28 @@ def test_multiline_ctrl_z(self):
366367
self.assertEqual(reader.cxy, (2, 3))
367368
con.restore()
368369

370+
def test_reset_on_finish(self):
371+
# gh-152068: finish() must emit the ANSI reset sequence so any
372+
# active color does not leak past the prompt.
373+
code = "1"
374+
events = code_to_events(code)
375+
_, con = self.handle_events(events)
376+
con.finish()
377+
con.out.write.assert_any_call(ANSIColors.RESET.encode(con.encoding))
378+
con.restore()
379+
380+
def test_reset_on_restore(self):
381+
# gh-152068: restore() must emit the ANSI reset sequence when VT
382+
# support is enabled.
383+
code = "1"
384+
events = code_to_events(code)
385+
_, con = self.handle_events(events)
386+
con._WindowsConsole__vt_support = True
387+
con._WindowsConsole__original_input_mode = 0
388+
with patch.object(wc, "SetConsoleMode", return_value=1):
389+
con.restore()
390+
con.out.write.assert_any_call(ANSIColors.RESET.encode(con.encoding))
391+
369392

370393
class WindowsConsoleGetEventTests(TestCase):
371394
# Virtual-Key Codes: https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixes a bug when a line was split (particularly on macOS Terminal.app) in the middle of a colorized keyword, causing the ANSI Color Reset sequence (ESC0m) to not be properly printed, causing the output to be colored when it shouldn't

0 commit comments

Comments
Β (0)