-
-
Notifications
You must be signed in to change notification settings - Fork 30.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
1 parent
e03dde5
commit f49df4f
Showing
11 changed files
with
1,517 additions
and
1,490 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import os | ||
from test.support import requires, load_package_tests | ||
from test.support.import_helper import import_module | ||
|
||
# Optionally test pyrepl. This currently requires that the | ||
# 'curses' resource be given on the regrtest command line using the -u | ||
# option. Additionally, we need to attempt to import curses and readline. | ||
requires("curses") | ||
curses = import_module("curses") | ||
readline = import_module("readline") | ||
|
||
|
||
def load_tests(*args): | ||
return load_package_tests(os.path.dirname(__file__), *args) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import unittest | ||
|
||
from _pyrepl.keymap import parse_keys, compile_keymap | ||
|
||
|
||
class TestParseKeys(unittest.TestCase): | ||
def test_single_character(self): | ||
self.assertEqual(parse_keys("a"), ["a"]) | ||
self.assertEqual(parse_keys("b"), ["b"]) | ||
self.assertEqual(parse_keys("1"), ["1"]) | ||
|
||
def test_escape_sequences(self): | ||
self.assertEqual(parse_keys("\\n"), ["\n"]) | ||
self.assertEqual(parse_keys("\\t"), ["\t"]) | ||
self.assertEqual(parse_keys("\\\\"), ["\\"]) | ||
self.assertEqual(parse_keys("\\'"), ["'"]) | ||
self.assertEqual(parse_keys('\\"'), ['"']) | ||
|
||
def test_control_sequences(self): | ||
self.assertEqual(parse_keys("\\C-a"), ["\x01"]) | ||
self.assertEqual(parse_keys("\\C-b"), ["\x02"]) | ||
self.assertEqual(parse_keys("\\C-c"), ["\x03"]) | ||
|
||
def test_meta_sequences(self): | ||
self.assertEqual(parse_keys("\\M-a"), ["\033", "a"]) | ||
self.assertEqual(parse_keys("\\M-b"), ["\033", "b"]) | ||
self.assertEqual(parse_keys("\\M-c"), ["\033", "c"]) | ||
|
||
def test_keynames(self): | ||
self.assertEqual(parse_keys("\\<up>"), ["up"]) | ||
self.assertEqual(parse_keys("\\<down>"), ["down"]) | ||
self.assertEqual(parse_keys("\\<left>"), ["left"]) | ||
self.assertEqual(parse_keys("\\<right>"), ["right"]) | ||
|
||
def test_combinations(self): | ||
self.assertEqual(parse_keys("\\C-a\\n\\<up>"), ["\x01", "\n", "up"]) | ||
self.assertEqual(parse_keys("\\M-a\\t\\<down>"), ["\033", "a", "\t", "down"]) | ||
|
||
|
||
class TestCompileKeymap(unittest.TestCase): | ||
def test_empty_keymap(self): | ||
keymap = {} | ||
result = compile_keymap(keymap) | ||
self.assertEqual(result, {}) | ||
|
||
def test_single_keymap(self): | ||
keymap = {b"a": "action"} | ||
result = compile_keymap(keymap) | ||
self.assertEqual(result, {b"a": "action"}) | ||
|
||
def test_nested_keymap(self): | ||
keymap = {b"a": {b"b": "action"}} | ||
result = compile_keymap(keymap) | ||
self.assertEqual(result, {b"a": {b"b": "action"}}) | ||
|
||
def test_empty_value(self): | ||
keymap = {b"a": {b"": "action"}} | ||
result = compile_keymap(keymap) | ||
self.assertEqual(result, {b"a": {b"": "action"}}) | ||
|
||
def test_multiple_empty_values(self): | ||
keymap = {b"a": {b"": "action1", b"b": "action2"}} | ||
result = compile_keymap(keymap) | ||
self.assertEqual(result, {b"a": {b"": "action1", b"b": "action2"}}) | ||
|
||
def test_multiple_keymaps(self): | ||
keymap = {b"a": {b"b": "action1", b"c": "action2"}} | ||
result = compile_keymap(keymap) | ||
self.assertEqual(result, {b"a": {b"b": "action1", b"c": "action2"}}) | ||
|
||
def test_nested_multiple_keymaps(self): | ||
keymap = {b"a": {b"b": {b"c": "action"}}} | ||
result = compile_keymap(keymap) | ||
self.assertEqual(result, {b"a": {b"b": {b"c": "action"}}}) |
Oops, something went wrong.