From 227038a454943a075c51b60988c53f77a605fa10 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 26 Feb 2021 05:10:02 +0100 Subject: Adding upstream version 3.0.16. Signed-off-by: Daniel Baumann --- ptpython/ipython.py | 285 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 285 insertions(+) create mode 100644 ptpython/ipython.py (limited to 'ptpython/ipython.py') diff --git a/ptpython/ipython.py b/ptpython/ipython.py new file mode 100644 index 0000000..2e8d119 --- /dev/null +++ b/ptpython/ipython.py @@ -0,0 +1,285 @@ +""" + +Adaptor for using the input system of `prompt_toolkit` with the IPython +backend. + +This gives a powerful interactive shell that has a nice user interface, but +also the power of for instance all the %-magic functions that IPython has to +offer. + +""" +from warnings import warn + +from IPython import utils as ipy_utils +from IPython.core.inputsplitter import IPythonInputSplitter +from IPython.terminal.embed import InteractiveShellEmbed as _InteractiveShellEmbed +from IPython.terminal.ipapp import load_default_config +from prompt_toolkit.completion import ( + Completer, + Completion, + PathCompleter, + WordCompleter, +) +from prompt_toolkit.contrib.completers import SystemCompleter +from prompt_toolkit.contrib.regular_languages.compiler import compile +from prompt_toolkit.contrib.regular_languages.completion import GrammarCompleter +from prompt_toolkit.contrib.regular_languages.lexer import GrammarLexer +from prompt_toolkit.document import Document +from prompt_toolkit.formatted_text import PygmentsTokens +from prompt_toolkit.lexers import PygmentsLexer, SimpleLexer +from prompt_toolkit.styles import Style +from pygments.lexers import BashLexer, PythonLexer + +from ptpython.prompt_style import PromptStyle + +from .python_input import PythonCompleter, PythonInput, PythonValidator +from .style import default_ui_style + +__all__ = ["embed"] + + +class IPythonPrompt(PromptStyle): + """ + Style for IPython >5.0, use the prompt_toolkit tokens directly. + """ + + def __init__(self, prompts): + self.prompts = prompts + + def in_prompt(self): + return PygmentsTokens(self.prompts.in_prompt_tokens()) + + def in2_prompt(self, width): + return PygmentsTokens(self.prompts.continuation_prompt_tokens()) + + def out_prompt(self): + return [] + + +class IPythonValidator(PythonValidator): + def __init__(self, *args, **kwargs): + super(IPythonValidator, self).__init__(*args, **kwargs) + self.isp = IPythonInputSplitter() + + def validate(self, document): + document = Document(text=self.isp.transform_cell(document.text)) + super(IPythonValidator, self).validate(document) + + +def create_ipython_grammar(): + """ + Return compiled IPython grammar. + """ + return compile( + r""" + \s* + ( + (?P%)( + (?Ppycat|run|loadpy|load) \s+ (?P[^\s]+) | + (?Pcat) \s+ (?P[^\s]+) | + (?Ppushd|cd|ls) \s+ (?P[^\s]+) | + (?Ppdb) \s+ (?P[^\s]+) | + (?Pautocall) \s+ (?P[^\s]+) | + (?Ptime|timeit|prun) \s+ (?P.+) | + (?Ppsource|pfile|pinfo|pinfo2) \s+ (?P.+) | + (?Psystem) \s+ (?P.+) | + (?Punalias) \s+ (?P.+) | + (?P[^\s]+) .* | + ) .* | + !(?P.+) | + (?![%!]) (?P.+) + ) + \s* + """ + ) + + +def create_completer( + get_globals, + get_locals, + magics_manager, + alias_manager, + get_enable_dictionary_completion, +): + g = create_ipython_grammar() + + return GrammarCompleter( + g, + { + "python": PythonCompleter( + get_globals, get_locals, get_enable_dictionary_completion + ), + "magic": MagicsCompleter(magics_manager), + "alias_name": AliasCompleter(alias_manager), + "pdb_arg": WordCompleter(["on", "off"], ignore_case=True), + "autocall_arg": WordCompleter(["0", "1", "2"], ignore_case=True), + "py_filename": PathCompleter( + only_directories=False, file_filter=lambda name: name.endswith(".py") + ), + "filename": PathCompleter(only_directories=False), + "directory": PathCompleter(only_directories=True), + "system": SystemCompleter(), + }, + ) + + +def create_lexer(): + g = create_ipython_grammar() + + return GrammarLexer( + g, + lexers={ + "percent": SimpleLexer("class:pygments.operator"), + "magic": SimpleLexer("class:pygments.keyword"), + "filename": SimpleLexer("class:pygments.name"), + "python": PygmentsLexer(PythonLexer), + "system": PygmentsLexer(BashLexer), + }, + ) + + +class MagicsCompleter(Completer): + def __init__(self, magics_manager): + self.magics_manager = magics_manager + + def get_completions(self, document, complete_event): + text = document.text_before_cursor.lstrip() + + for m in sorted(self.magics_manager.magics["line"]): + if m.startswith(text): + yield Completion("%s" % m, -len(text)) + + +class AliasCompleter(Completer): + def __init__(self, alias_manager): + self.alias_manager = alias_manager + + def get_completions(self, document, complete_event): + text = document.text_before_cursor.lstrip() + # aliases = [a for a, _ in self.alias_manager.aliases] + aliases = self.alias_manager.aliases + + for a, cmd in sorted(aliases, key=lambda a: a[0]): + if a.startswith(text): + yield Completion("%s" % a, -len(text), display_meta=cmd) + + +class IPythonInput(PythonInput): + """ + Override our `PythonCommandLineInterface` to add IPython specific stuff. + """ + + def __init__(self, ipython_shell, *a, **kw): + kw["_completer"] = create_completer( + kw["get_globals"], + kw["get_globals"], + ipython_shell.magics_manager, + ipython_shell.alias_manager, + lambda: self.enable_dictionary_completion, + ) + kw["_lexer"] = create_lexer() + kw["_validator"] = IPythonValidator(get_compiler_flags=self.get_compiler_flags) + + super().__init__(*a, **kw) + self.ipython_shell = ipython_shell + + self.all_prompt_styles["ipython"] = IPythonPrompt(ipython_shell.prompts) + self.prompt_style = "ipython" + + # UI style for IPython. Add tokens that are used by IPython>5.0 + style_dict = {} + style_dict.update(default_ui_style) + style_dict.update( + { + "pygments.prompt": "#009900", + "pygments.prompt-num": "#00ff00 bold", + "pygments.out-prompt": "#990000", + "pygments.out-prompt-num": "#ff0000 bold", + } + ) + + self.ui_styles = {"default": Style.from_dict(style_dict)} + self.use_ui_colorscheme("default") + + +class InteractiveShellEmbed(_InteractiveShellEmbed): + """ + Override the `InteractiveShellEmbed` from IPython, to replace the front-end + with our input shell. + + :param configure: Callable for configuring the repl. + """ + + def __init__(self, *a, **kw): + vi_mode = kw.pop("vi_mode", False) + history_filename = kw.pop("history_filename", None) + configure = kw.pop("configure", None) + title = kw.pop("title", None) + + # Don't ask IPython to confirm for exit. We have our own exit prompt. + self.confirm_exit = False + + super().__init__(*a, **kw) + + def get_globals(): + return self.user_ns + + python_input = IPythonInput( + self, + get_globals=get_globals, + vi_mode=vi_mode, + history_filename=history_filename, + ) + + if title: + python_input.terminal_title = title + + if configure: + configure(python_input) + python_input.prompt_style = "ipython" # Don't take from config. + + self.python_input = python_input + + def prompt_for_code(self): + try: + return self.python_input.app.run() + except KeyboardInterrupt: + self.python_input.default_buffer.document = Document() + return "" + + +def initialize_extensions(shell, extensions): + """ + Partial copy of `InteractiveShellApp.init_extensions` from IPython. + """ + try: + iter(extensions) + except TypeError: + pass # no extensions found + else: + for ext in extensions: + try: + shell.extension_manager.load_extension(ext) + except: + warn( + "Error in loading extension: %s" % ext + + "\nCheck your config files in %s" + % ipy_utils.path.get_ipython_dir() + ) + shell.showtraceback() + + +def embed(**kwargs): + """ + Copied from `IPython/terminal/embed.py`, but using our `InteractiveShellEmbed` instead. + """ + config = kwargs.get("config") + header = kwargs.pop("header", "") + compile_flags = kwargs.pop("compile_flags", None) + if config is None: + config = load_default_config() + config.InteractiveShellEmbed = config.TerminalInteractiveShell + kwargs["config"] = config + shell = InteractiveShellEmbed.instance(**kwargs) + initialize_extensions(shell, config["InteractiveShellApp"]["extensions"]) + shell(header=header, stack_depth=2, compile_flags=compile_flags) -- cgit v1.2.3