from dataclasses import dataclass, field
from typing import Literal, Protocol, Self
-from .utils import ANSI_ESCAPE_SEQUENCE, THEME, StyleRef, str_width
+from _colorize import ANSIColors
+
from .types import CursorXY
+from .utils import ANSI_ESCAPE_SEQUENCE, THEME, StyleRef, str_width
type RenderStyle = StyleRef | str | None
type LineUpdateKind = Literal[
def _update_terminal_state(state: str, escape: str) -> str:
- if escape in {"\x1b[0m", "\x1b[m"}:
+ if escape in {ANSIColors.RESET, "\x1b[m"}:
return ""
return state + escape
target_escape += visual_style
if target_escape != active_escape:
if active_escape:
- rendered.append("\x1b[0m")
+ rendered.append(ANSIColors.RESET)
if target_escape:
rendered.append(target_escape)
active_escape = target_escape
rendered.append(cell.text)
if active_escape:
- rendered.append("\x1b[0m")
+ rendered.append(ANSIColors.RESET)
return "".join(rendered)
from fcntl import ioctl
from typing import TYPE_CHECKING, overload
+from _colorize import ANSIColors
+
from . import terminfo
from .console import Console, Event
from .fancy_termios import tcgetattr, tcsetattr, TermState
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()
while y >= 0 and not rendered_lines[y].text:
y -= 1
self.__move(0, min(y, self.height + self.__offset - 1))
+ self.__write(ANSIColors.RESET)
self.__write("\n\r")
self.flushoutput()
)
from ctypes import Structure, POINTER, Union
from typing import TYPE_CHECKING
+
+from _colorize import ANSIColors
+
from .console import Event, Console
from .render import (
EMPTY_RENDER_LINE,
def restore(self) -> None:
trace("windows.restore")
if self.__vt_support:
+ self.__write(ANSIColors.RESET)
# Recover to original mode before running REPL
self._disable_bracketed_paste()
if not SetConsoleMode(InHandle, self.__original_input_mode):
while y >= 0 and not rendered_lines[y].text:
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:
import threading
import unittest
from functools import partial
+from _colorize import ANSIColors
from test.support import force_color, os_helper, force_not_colorized_test_class
from test.support import threading_helper
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)
import itertools
+from _colorize import ANSIColors
from functools import partial
from test.support import force_not_colorized_test_class
from typing import Iterable
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))
+
@patch.object(WindowsConsole, '__init__', _mock_console_init)
class WindowsConsoleGetEventTests(TestCase):
--- /dev/null
+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