aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Lib/_pyrepl/simple_interact.py
diff options
context:
space:
mode:
authorPablo Galindo Salgado <Pablogsal@gmail.com>2024-05-05 21:32:23 +0200
committerGitHub <noreply@github.com>2024-05-05 21:32:23 +0200
commitf27f8c790af1233d499b795af1c0d1b36aaecaf5 (patch)
tree22c502c6382512fafbb63e3020c8462e5400d4df /Lib/_pyrepl/simple_interact.py
parent40cc809902304f60c6e1c933191dd4d64e570e28 (diff)
downloadcpython-f27f8c790af1233d499b795af1c0d1b36aaecaf5.tar.gz
cpython-f27f8c790af1233d499b795af1c0d1b36aaecaf5.zip
gh-111201: A new Python REPL (GH-111567)
Co-authored-by: Łukasz Langa <lukasz@langa.pl> Co-authored-by: Marta Gómez Macías <mgmacias@google.com> Co-authored-by: Lysandros Nikolaou <lisandrosnik@gmail.com> Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
Diffstat (limited to 'Lib/_pyrepl/simple_interact.py')
-rw-r--r--Lib/_pyrepl/simple_interact.py157
1 files changed, 157 insertions, 0 deletions
diff --git a/Lib/_pyrepl/simple_interact.py b/Lib/_pyrepl/simple_interact.py
new file mode 100644
index 00000000000..ce79d0d547e
--- /dev/null
+++ b/Lib/_pyrepl/simple_interact.py
@@ -0,0 +1,157 @@
+# Copyright 2000-2010 Michael Hudson-Doyle <micahel@gmail.com>
+# Armin Rigo
+#
+# All Rights Reserved
+#
+#
+# Permission to use, copy, modify, and distribute this software and
+# its documentation for any purpose is hereby granted without fee,
+# provided that the above copyright notice appear in all copies and
+# that both that copyright notice and this permission notice appear in
+# supporting documentation.
+#
+# THE AUTHOR MICHAEL HUDSON DISCLAIMS ALL WARRANTIES WITH REGARD TO
+# THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
+# INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+# RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+# CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""This is an alternative to python_reader which tries to emulate
+the CPython prompt as closely as possible, with the exception of
+allowing multiline input and multiline history entries.
+"""
+
+from __future__ import annotations
+
+import _colorize # type: ignore[import-not-found]
+import _sitebuiltins
+import linecache
+import sys
+import code
+from types import ModuleType
+
+from .console import Event
+from .readline import _get_reader, multiline_input
+from .unix_console import _error
+
+
+def check() -> str:
+ """Returns the error message if there is a problem initializing the state."""
+ try:
+ _get_reader()
+ except _error as e:
+ return str(e) or repr(e) or "unknown error"
+ return ""
+
+
+def _strip_final_indent(text: str) -> str:
+ # kill spaces and tabs at the end, but only if they follow '\n'.
+ # meant to remove the auto-indentation only (although it would of
+ # course also remove explicitly-added indentation).
+ short = text.rstrip(" \t")
+ n = len(short)
+ if n > 0 and text[n - 1] == "\n":
+ return short
+ return text
+
+
+REPL_COMMANDS = {
+ "exit": _sitebuiltins.Quitter('exit', ''),
+ "quit": _sitebuiltins.Quitter('quit' ,''),
+ "copyright": _sitebuiltins._Printer('copyright', sys.copyright),
+ "help": "help",
+}
+
+class InteractiveColoredConsole(code.InteractiveConsole):
+ def __init__(
+ self,
+ locals: dict[str, object] | None = None,
+ filename: str = "<console>",
+ *,
+ local_exit: bool = False,
+ ) -> None:
+ super().__init__(locals=locals, filename=filename, local_exit=local_exit) # type: ignore[call-arg]
+ self.can_colorize = _colorize.can_colorize()
+
+ def showtraceback(self):
+ super().showtraceback(colorize=self.can_colorize)
+
+
+def run_multiline_interactive_console(
+ mainmodule: ModuleType | None= None, future_flags: int = 0
+) -> None:
+ import code
+ import __main__
+ from .readline import _setup
+ _setup()
+
+ mainmodule = mainmodule or __main__
+ console = InteractiveColoredConsole(mainmodule.__dict__, filename="<stdin>")
+ if future_flags:
+ console.compile.compiler.flags |= future_flags
+
+ input_n = 0
+
+ def maybe_run_command(statement: str) -> bool:
+ statement = statement.strip()
+ if statement in console.locals or statement not in REPL_COMMANDS:
+ return False
+
+ reader = _get_reader()
+ reader.history.pop() # skip internal commands in history
+ command = REPL_COMMANDS[statement]
+ if callable(command):
+ command()
+ return True
+
+ if isinstance(command, str):
+ # Internal readline commands require a prepared reader like
+ # inside multiline_input.
+ reader.prepare()
+ reader.refresh()
+ reader.do_cmd((command, [statement]))
+ reader.restore()
+ return True
+
+ return False
+
+ def more_lines(unicodetext: str) -> bool:
+ # ooh, look at the hack:
+ src = _strip_final_indent(unicodetext)
+ try:
+ code = console.compile(src, "<stdin>", "single")
+ except (OverflowError, SyntaxError, ValueError):
+ return False
+ else:
+ return code is None
+
+ while 1:
+ try:
+ try:
+ sys.stdout.flush()
+ except Exception:
+ pass
+
+ ps1 = getattr(sys, "ps1", ">>> ")
+ ps2 = getattr(sys, "ps2", "... ")
+ try:
+ statement = multiline_input(more_lines, ps1, ps2)
+ except EOFError:
+ break
+
+ if maybe_run_command(statement):
+ continue
+
+ input_name = f"<python-input-{input_n}>"
+ linecache._register_code(input_name, statement, "<stdin>") # type: ignore[attr-defined]
+ more = console.push(_strip_final_indent(statement), filename=input_name) # type: ignore[call-arg]
+ assert not more
+ input_n += 1
+ except KeyboardInterrupt:
+ console.write("\nKeyboardInterrupt\n")
+ console.resetbuffer()
+ except MemoryError:
+ console.write("\nMemoryError\n")
+ console.resetbuffer()