https://github.com/python/cpython/commit/bc7cc2448869f281f19aabb65284ba571095c1c2
commit: bc7cc2448869f281f19aabb65284ba571095c1c2
branch: 3.14
author: Eduardo Villalpando Mello <[email protected]>
committer: pablogsal <[email protected]>
date: 2026-09-24T17:22:18+01:00
summary:
[3.14] gh-152068: Reset PyREPL Colors on prompt finish (GH-152108) (#154402)
(cherry picked from commit 832d5571115fba0adcb09742b56f51ee081710c6)
files:
A Misc/NEWS.d/next/Library/2026-07-02-19-21-15.gh-issue-152068.ThsmJU.rst
M Lib/_pyrepl/unix_console.py
M Lib/_pyrepl/windows_console.py
M Lib/test/test_pyrepl/test_unix_console.py
M Lib/test/test_pyrepl/test_windows_console.py
diff --git a/Lib/_pyrepl/unix_console.py b/Lib/_pyrepl/unix_console.py
index 639d16db3f88d4..6b97b2e53bcfca 100644
--- a/Lib/_pyrepl/unix_console.py
+++ b/Lib/_pyrepl/unix_console.py
@@ -33,6 +33,8 @@
import platform
from fcntl import ioctl
+from _colorize import ANSIColors
+
from . import terminfo
from .console import Console, Event
from .fancy_termios import tcgetattr, tcsetattr, TermState
@@ -382,6 +384,8 @@ def restore(self):
"""
Restore the console to the default state
"""
+ trace("unix.restore")
+ self.__write(ANSIColors.RESET)
self.__disable_bracketed_paste()
self.__maybe_write_code(self._rmkx)
self.flushoutput()
@@ -518,6 +522,7 @@ def finish(self):
while y >= 0 and not self.screen[y]:
y -= 1
self.__move(0, min(y, self.height + self.__offset - 1))
+ self.__write(ANSIColors.RESET)
self.__write("\n\r")
self.flushoutput()
diff --git a/Lib/_pyrepl/windows_console.py b/Lib/_pyrepl/windows_console.py
index 46c6030748b8a6..09be054183eaf9 100644
--- a/Lib/_pyrepl/windows_console.py
+++ b/Lib/_pyrepl/windows_console.py
@@ -37,6 +37,9 @@
SHORT,
)
from ctypes import Structure, POINTER, Union
+
+from _colorize import ANSIColors
+
from .console import Event, Console
from .trace import trace
from .utils import wlen
@@ -363,6 +366,7 @@ def prepare(self) -> None:
def restore(self) -> None:
if self.__vt_support:
+ self.__write(ANSIColors.RESET)
# Recover to original mode before running REPL
self._disable_bracketed_paste()
SetConsoleMode(InHandle, self.__original_input_mode)
@@ -519,6 +523,7 @@ def finish(self) -> None:
while y >= 0 and not self.screen[y]:
y -= 1
self._move_relative(0, min(y, self.height + self.__offset - 1))
+ self.__write(ANSIColors.RESET)
self.__write("\r\n")
def flushoutput(self) -> None:
diff --git a/Lib/test/test_pyrepl/test_unix_console.py
b/Lib/test/test_pyrepl/test_unix_console.py
index 8198d489188f1e..6511e280d5ae48 100644
--- a/Lib/test/test_pyrepl/test_unix_console.py
+++ b/Lib/test/test_pyrepl/test_unix_console.py
@@ -6,6 +6,7 @@
import threading
import unittest
from functools import partial
+from _colorize import ANSIColors
from test.support import os_helper, force_not_colorized_test_class
from test.support import threading_helper
@@ -107,6 +108,24 @@ def test_no_newline(self, _os_write):
self.assertNotIn(call(ANY, b'\n'), _os_write.mock_calls)
con.restore()
+ def test_reset_on_finish(self, _os_write):
+ # gh-152068: finish() must emit the ANSI reset sequence so any
+ # active color does not leak past the prompt.
+ code = "1"
+ events = code_to_events(code)
+ _, con = handle_events_unix_console(events)
+ con.finish()
+ _os_write.assert_any_call(ANY, ANSIColors.RESET.encode(con.encoding))
+ con.restore()
+
+ def test_reset_on_restore(self, _os_write):
+ # gh-152068: restore() must emit the ANSI reset sequence.
+ code = "1"
+ events = code_to_events(code)
+ _, con = handle_events_unix_console(events)
+ con.restore()
+ _os_write.assert_any_call(ANY, ANSIColors.RESET.encode(con.encoding))
+
def test_newline(self, _os_write):
code = "\n"
events = code_to_events(code)
diff --git a/Lib/test/test_pyrepl/test_windows_console.py
b/Lib/test/test_pyrepl/test_windows_console.py
index 518ddee1125397..54905e605f3ff8 100644
--- a/Lib/test/test_pyrepl/test_windows_console.py
+++ b/Lib/test/test_pyrepl/test_windows_console.py
@@ -6,11 +6,12 @@
import itertools
+from _colorize import ANSIColors
from functools import partial
from test.support import force_not_colorized_test_class
from typing import Iterable
from unittest import TestCase
-from unittest.mock import MagicMock, call
+from unittest.mock import MagicMock, call, patch
from .support import handle_all_events, code_to_events
from .support import prepare_reader as default_prepare_reader
@@ -366,6 +367,28 @@ def test_multiline_ctrl_z(self):
self.assertEqual(reader.cxy, (2, 3))
con.restore()
+ def test_reset_on_finish(self):
+ # gh-152068: finish() must emit the ANSI reset sequence so any
+ # active color does not leak past the prompt.
+ code = "1"
+ events = code_to_events(code)
+ _, con = self.handle_events(events)
+ con.finish()
+ con.out.write.assert_any_call(ANSIColors.RESET.encode(con.encoding))
+ con.restore()
+
+ def test_reset_on_restore(self):
+ # gh-152068: restore() must emit the ANSI reset sequence when VT
+ # support is enabled.
+ code = "1"
+ events = code_to_events(code)
+ _, con = self.handle_events(events)
+ con._WindowsConsole__vt_support = True
+ con._WindowsConsole__original_input_mode = 0
+ with patch.object(wc, "SetConsoleMode", return_value=1):
+ con.restore()
+ con.out.write.assert_any_call(ANSIColors.RESET.encode(con.encoding))
+
class WindowsConsoleGetEventTests(TestCase):
# Virtual-Key Codes:
https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
diff --git
a/Misc/NEWS.d/next/Library/2026-07-02-19-21-15.gh-issue-152068.ThsmJU.rst
b/Misc/NEWS.d/next/Library/2026-07-02-19-21-15.gh-issue-152068.ThsmJU.rst
new file mode 100644
index 00000000000000..9ec8413b4ac424
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-07-02-19-21-15.gh-issue-152068.ThsmJU.rst
@@ -0,0 +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
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]