gh-119306: Break up _pyrepl tests by eugenetriguba · Pull Request #119307 · python/cpython · GitHub
Skip to content
Merged
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
1,490 changes: 0 additions & 1,490 deletions Lib/test/test_pyrepl.py

This file was deleted.

14 changes: 14 additions & 0 deletions Lib/test/test_pyrepl/__init__.py
4 changes: 4 additions & 0 deletions Lib/test/test_pyrepl/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import unittest
from test.test_pyrepl import load_tests

unittest.main()
141 changes: 141 additions & 0 deletions Lib/test/test_pyrepl/support.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
from code import InteractiveConsole
from functools import partial
from typing import Iterable
from unittest.mock import MagicMock

from _pyrepl.console import Console, Event
from _pyrepl.readline import ReadlineAlikeReader, ReadlineConfig
from _pyrepl.simple_interact import _strip_final_indent


def multiline_input(reader: ReadlineAlikeReader, namespace: dict | None = None):
saved = reader.more_lines
try:
reader.more_lines = partial(more_lines, namespace=namespace)
reader.ps1 = reader.ps2 = ">>>"
reader.ps3 = reader.ps4 = "..."
return reader.readline()
finally:
reader.more_lines = saved
reader.paste_mode = False


def more_lines(text: str, namespace: dict | None = None):
if namespace is None:
namespace = {}
src = _strip_final_indent(text)
console = InteractiveConsole(namespace, filename="<stdin>")
try:
code = console.compile(src, "<stdin>", "single")
except (OverflowError, SyntaxError, ValueError):
return False
else:
return code is None


def code_to_events(code: str):
for c in code:
yield Event(evt="key", data=c, raw=bytearray(c.encode("utf-8")))


def prepare_reader(console: Console, **kwargs):
config = ReadlineConfig(readline_completer=None)
reader = ReadlineAlikeReader(console=console, config=config)
reader.more_lines = partial(more_lines, namespace=None)
reader.paste_mode = True # Avoid extra indents

def get_prompt(lineno, cursor_on_line) -> str:
return ""

reader.get_prompt = get_prompt # Remove prompt for easier calculations of (x, y)

for key, val in kwargs.items():
setattr(reader, key, val)

return reader


def prepare_console(events: Iterable[Event], **kwargs):
console = MagicMock()
console.get_event.side_effect = events
console.height = 100
console.width = 80
for key, val in kwargs.items():
setattr(console, key, val)
return console


def handle_all_events(
events, prepare_console=prepare_console, prepare_reader=prepare_reader
):
console = prepare_console(events)
reader = prepare_reader(console)
try:
while True:
reader.handle1()
except StopIteration:
pass
return reader, console


handle_events_narrow_console = partial(
handle_all_events,
prepare_console=partial(prepare_console, width=10),
)


class FakeConsole(Console):
def __init__(self, events, encoding="utf-8"):
self.events = iter(events)
self.encoding = encoding
self.screen = []
self.height = 100
self.width = 80

def get_event(self, block: bool = True) -> Event | None:
return next(self.events)

def getpending(self) -> Event:
return self.get_event(block=False)

def getheightwidth(self) -> tuple[int, int]:
return self.height, self.width

def refresh(self, screen: list[str], xy: tuple[int, int]) -> None:
pass

def prepare(self) -> None:
pass

def restore(self) -> None:
pass

def move_cursor(self, x: int, y: int) -> None:
pass

def set_cursor_vis(self, visible: bool) -> None:
pass

def push_char(self, char: int | bytes) -> None:
pass

def beep(self) -> None:
pass

def clear(self) -> None:
pass

def finish(self) -> None:
pass

def flushoutput(self) -> None:
pass

def forgetinput(self) -> None:
pass

def wait(self) -> None:
pass

def repaint(self) -> None:
pass
102 changes: 102 additions & 0 deletions Lib/test/test_pyrepl/test_input.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import unittest

from _pyrepl.console import Event
from _pyrepl.input import KeymapTranslator


class KeymapTranslatorTests(unittest.TestCase):
def test_push_single_key(self):
keymap = [("a", "command_a")]
translator = KeymapTranslator(keymap)
evt = Event("key", "a")
translator.push(evt)
result = translator.get()
self.assertEqual(result, ("command_a", ["a"]))

def test_push_multiple_keys(self):
keymap = [("ab", "command_ab")]
translator = KeymapTranslator(keymap)
evt1 = Event("key", "a")
evt2 = Event("key", "b")
translator.push(evt1)
translator.push(evt2)
result = translator.get()
self.assertEqual(result, ("command_ab", ["a", "b"]))

def test_push_invalid_key(self):
keymap = [("a", "command_a")]
translator = KeymapTranslator(keymap)
evt = Event("key", "b")
translator.push(evt)
result = translator.get()
self.assertEqual(result, (None, ["b"]))

def test_push_invalid_key_with_stack(self):
keymap = [("ab", "command_ab")]
translator = KeymapTranslator(keymap)
evt1 = Event("key", "a")
evt2 = Event("key", "c")
translator.push(evt1)
translator.push(evt2)
result = translator.get()
self.assertEqual(result, (None, ["a", "c"]))

def test_push_character_key(self):
keymap = [("a", "command_a")]
translator = KeymapTranslator(keymap)
evt = Event("key", "a")
translator.push(evt)
result = translator.get()
self.assertEqual(result, ("command_a", ["a"]))

def test_push_character_key_with_stack(self):
keymap = [("ab", "command_ab")]
translator = KeymapTranslator(keymap)
evt1 = Event("key", "a")
evt2 = Event("key", "b")
evt3 = Event("key", "c")
translator.push(evt1)
translator.push(evt2)
translator.push(evt3)
result = translator.get()
self.assertEqual(result, ("command_ab", ["a", "b"]))

def test_push_transition_key(self):
keymap = [("a", {"b": "command_ab"})]
translator = KeymapTranslator(keymap)
evt1 = Event("key", "a")
evt2 = Event("key", "b")
translator.push(evt1)
translator.push(evt2)
result = translator.get()
self.assertEqual(result, ("command_ab", ["a", "b"]))

def test_push_transition_key_interrupted(self):
keymap = [("a", {"b": "command_ab"})]
translator = KeymapTranslator(keymap)
evt1 = Event("key", "a")
evt2 = Event("key", "c")
evt3 = Event("key", "b")
translator.push(evt1)
translator.push(evt2)
translator.push(evt3)
result = translator.get()
self.assertEqual(result, (None, ["a", "c"]))

def test_push_invalid_key_with_unicode_category(self):
keymap = [("a", "command_a")]
translator = KeymapTranslator(keymap)
evt = Event("key", "\u0003") # Control character
translator.push(evt)
result = translator.get()
self.assertEqual(result, (None, ["\u0003"]))

def test_empty(self):
keymap = [("a", "command_a")]
translator = KeymapTranslator(keymap)
self.assertTrue(translator.empty())
evt = Event("key", "a")
translator.push(evt)
self.assertFalse(translator.empty())
translator.get()
self.assertTrue(translator.empty())
74 changes: 74 additions & 0 deletions Lib/test/test_pyrepl/test_keymap.py
Loading