Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Lib/_pyrepl/unix_console.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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()

Expand Down
6 changes: 6 additions & 0 deletions Lib/_pyrepl/windows_console.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
SHORT,
)
from ctypes import Structure, POINTER, Union
from typing import TYPE_CHECKING

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need this.


from _colorize import ANSIColors

from .console import Event, Console
from .trace import trace
from .utils import wlen
Expand Down Expand Up @@ -363,6 +367,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)
Expand Down Expand Up @@ -519,6 +524,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:
Expand Down
21 changes: 20 additions & 1 deletion Lib/test/test_pyrepl/test_unix_console.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
import threading
import unittest
from functools import partial
from test.support import os_helper, force_not_colorized_test_class
from _colorize import ANSIColors
from test.support import force_color, os_helper, force_not_colorized_test_class

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you import force_color here?

from test.support import threading_helper

from unittest import TestCase
Expand Down Expand Up @@ -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)
Expand Down
23 changes: 23 additions & 0 deletions Lib/test/test_pyrepl/test_windows_console.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@


import itertools
from _colorize import ANSIColors
from functools import partial
from test.support import force_not_colorized_test_class
from typing import Iterable
Expand Down Expand Up @@ -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):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

patch is undefined here

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
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Loading