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]

Reply via email to