mirror of
				https://github.com/python/cpython.git
				synced 2025-10-30 21:21:22 +00:00 
			
		
		
		
	
		
			
	
	
		
			142 lines
		
	
	
	
		
			3.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			142 lines
		
	
	
	
		
			3.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|   | 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 |