From 2436f9bd9d44d2b772aca672249013a04eda11db 2016-06-15 21:19:46 From: Min RK Date: 2016-06-15 21:19:46 Subject: [PATCH] undeprecate terminal.interactiveshell move terminal.ptshell to terminal.interactiveshell, since it is the new implementation of the class. The deprecation was there while the readline implementation remained, but there is no need for it now that there is just one implementation of TerminalInteractiveShell. --- diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index b5e52ce..3157d7c 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -365,22 +365,22 @@ class InteractiveShell(SingletonConfigurable): # deprecated prompt traits: prompt_in1 = Unicode('In [\\#]: ', - help="Deprecated since IPython 4.0 and ignored since 5.0, set IPython.terminal.ptshell.TerminalInteractiveShell.prompts object directly." + help="Deprecated since IPython 4.0 and ignored since 5.0, set TerminalInteractiveShell.prompts object directly." ).tag(config=True) prompt_in2 = Unicode(' .\\D.: ', - help="Deprecated since IPython 4.0 and ignored since 5.0, set IPython.terminal.ptshell.TerminalInteractiveShell.prompts object directly." + help="Deprecated since IPython 4.0 and ignored since 5.0, set TerminalInteractiveShell.prompts object directly." ).tag(config=True) prompt_out = Unicode('Out[\\#]: ', - help="Deprecated since IPython 4.0 and ignored since 5.0, set IPython.terminal.ptshell.TerminalInteractiveShell.prompts object directly." + help="Deprecated since IPython 4.0 and ignored since 5.0, set TerminalInteractiveShell.prompts object directly." ).tag(config=True) prompts_pad_left = Bool(True, - help="Deprecated since IPython 4.0 and ignored since 5.0, set IPython.terminal.ptshell.TerminalInteractiveShell.prompts object directly." + help="Deprecated since IPython 4.0 and ignored since 5.0, set TerminalInteractiveShell.prompts object directly." ).tag(config=True) @observe('prompt_in1', 'prompt_in2', 'prompt_out', 'prompt_pad_left') def _prompt_trait_changed(self, change): name = change['name'] - warn("InteractiveShell.{name} is deprecated since IPython 4.0 and ignored since 5.0, set IPython.terminal.ptshell.TerminalInteractiveShell.prompts object directly.".format( + warn("InteractiveShell.{name} is deprecated since IPython 4.0 and ignored since 5.0, set TerminalInteractiveShell.prompts object directly.".format( name=name) ) # protect against weird cases where self.config may not exist: diff --git a/IPython/terminal/embed.py b/IPython/terminal/embed.py index 9ada31c..275cace 100644 --- a/IPython/terminal/embed.py +++ b/IPython/terminal/embed.py @@ -13,9 +13,8 @@ import warnings from IPython.core import ultratb, compilerop from IPython.core.magic import Magics, magics_class, line_magic -from IPython.core.interactiveshell import DummyMod -from IPython.core.interactiveshell import InteractiveShell -from IPython.terminal.ptshell import TerminalInteractiveShell +from IPython.core.interactiveshell import DummyMod, InteractiveShell +from IPython.terminal.interactiveshell import TerminalInteractiveShell from IPython.terminal.ipapp import load_default_config from traitlets import Bool, CBool, Unicode diff --git a/IPython/terminal/interactiveshell.py b/IPython/terminal/interactiveshell.py index 5c89ca0..85c3a89 100644 --- a/IPython/terminal/interactiveshell.py +++ b/IPython/terminal/interactiveshell.py @@ -1,21 +1,569 @@ -# -*- coding: utf-8 -*- -"""DEPRECATED: old import location of TerminalInteractiveShell""" - -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. +"""IPython terminal interface using prompt_toolkit in place of readline""" +from __future__ import print_function +import os +import sys +import signal from warnings import warn -from IPython.utils.decorators import undoc -from .ptshell import TerminalInteractiveShell as PromptToolkitShell +from IPython.core.error import TryNext +from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC +from IPython.utils.py3compat import PY3, cast_unicode_py2, input +from IPython.utils.terminal import toggle_set_term_title, set_term_title +from IPython.utils.process import abbrev_cwd +from traitlets import Bool, Unicode, Dict, Integer, observe, Instance, Type, default + +from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER, EditingMode +from prompt_toolkit.filters import (HasFocus, HasSelection, Condition, + ViInsertMode, EmacsInsertMode, IsDone, HasCompletions) +from prompt_toolkit.filters.cli import ViMode +from prompt_toolkit.history import InMemoryHistory +from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout +from prompt_toolkit.interface import CommandLineInterface +from prompt_toolkit.key_binding.manager import KeyBindingManager +from prompt_toolkit.keys import Keys +from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor +from prompt_toolkit.styles import PygmentsStyle, DynamicStyle + +from pygments.styles import get_style_by_name, get_all_styles +from pygments.token import Token + +from .debugger import TerminalPdb, Pdb +from .magics import TerminalMagics +from .pt_inputhooks import get_inputhook_func +from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook +from .ptutils import IPythonPTCompleter, IPythonPTLexer + + +def get_default_editor(): + try: + ed = os.environ['EDITOR'] + if not PY3: + ed = ed.decode() + return ed + except KeyError: + pass + except UnicodeError: + warn("$EDITOR environment variable is not pure ASCII. Using platform " + "default editor.") + + if os.name == 'posix': + return 'vi' # the only one guaranteed to be there! + else: + return 'notepad' # same in Windows! + + +if sys.stdin and sys.stdout and sys.stderr: + _is_tty = (sys.stdin.isatty()) and (sys.stdout.isatty()) and (sys.stderr.isatty()) +else: + _is_tty = False + + +_use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty) + +class TerminalInteractiveShell(InteractiveShell): + colors_force = True + + space_for_menu = Integer(6, help='Number of line at the bottom of the screen ' + 'to reserve for the completion menu' + ).tag(config=True) + + def _space_for_menu_changed(self, old, new): + self._update_layout() + + pt_cli = None + debugger_history = None + + simple_prompt = Bool(_use_simple_prompt, + help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors. + + Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are: + IPython own testing machinery, and emacs inferior-shell integration through elpy. + + This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT` + environment variable is set, or the current terminal is not a tty. + + """ + ).tag(config=True) + + @property + def debugger_cls(self): + return Pdb if self.simple_prompt else TerminalPdb + + autoedit_syntax = Bool(False, + help="auto editing of files with syntax errors.", + ).tag(config=True) + + + confirm_exit = Bool(True, + help=""" + Set to confirm when you try to exit IPython with an EOF (Control-D + in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit', + you can force a direct exit without any confirmation.""", + ).tag(config=True) + + editing_mode = Unicode('emacs', + help="Shortcut style to use at the prompt. 'vi' or 'emacs'.", + ).tag(config=True) + + mouse_support = Bool(False, + help="Enable mouse support in the prompt" + ).tag(config=True) + + highlighting_style = Unicode('default', + help="The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles()) + ).tag(config=True) + + + @observe('highlighting_style') + def _highlighting_style_changed(self, change): + self._style = self._make_style_from_name(self.highlighting_style) + + highlighting_style_overrides = Dict( + help="Override highlighting format for specific tokens" + ).tag(config=True) + + editor = Unicode(get_default_editor(), + help="Set the editor used by IPython (default to $EDITOR/vi/notepad)." + ).tag(config=True) + + prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True) + + prompts = Instance(Prompts) + + @default('prompts') + def _prompts_default(self): + return self.prompts_class(self) + + @observe('prompts') + def _(self, change): + self._update_layout() + + @default('displayhook_class') + def _displayhook_class_default(self): + return RichPromptDisplayHook + + term_title = Bool(True, + help="Automatically set the terminal title" + ).tag(config=True) + + display_completions_in_columns = Bool(False, + help="Display a multi column completion menu.", + ).tag(config=True) + + highlight_matching_brackets = Bool(True, + help="Highlight matching brackets .", + ).tag(config=True) + + @observe('term_title') + def init_term_title(self, change=None): + # Enable or disable the terminal title. + if self.term_title: + toggle_set_term_title(True) + set_term_title('IPython: ' + abbrev_cwd()) + else: + toggle_set_term_title(False) + + def init_display_formatter(self): + super(TerminalInteractiveShell, self).init_display_formatter() + # terminal only supports plain text + self.display_formatter.active_types = ['text/plain'] + + def init_prompt_toolkit_cli(self): + self._app = None + if self.simple_prompt: + # Fall back to plain non-interactive output for tests. + # This is very limited, and only accepts a single line. + def prompt(): + return cast_unicode_py2(input('In [%d]: ' % self.execution_count)) + self.prompt_for_code = prompt + return + + kbmanager = KeyBindingManager.for_prompt() + insert_mode = ViInsertMode() | EmacsInsertMode() + # Ctrl+J == Enter, seemingly + @kbmanager.registry.add_binding(Keys.ControlJ, + filter=(HasFocus(DEFAULT_BUFFER) + & ~HasSelection() + & insert_mode + )) + def _(event): + b = event.current_buffer + d = b.document + + if b.complete_state: + cc = b.complete_state.current_completion + if cc: + b.apply_completion(cc) + else: + b.cancel_completion() + return + + if not (d.on_last_line or d.cursor_position_row >= d.line_count + - d.empty_line_count_at_the_end()): + b.newline() + return + + status, indent = self.input_splitter.check_complete(d.text + '\n') + + if (status != 'incomplete') and b.accept_action.is_returnable: + b.accept_action.validate_and_handle(event.cli, b) + else: + b.insert_text('\n' + (' ' * (indent or 0))) + + @kbmanager.registry.add_binding(Keys.ControlP, filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER))) + def _previous_history_or_previous_completion(event): + """ + Control-P in vi edit mode on readline is history next, unlike default prompt toolkit. + + If completer is open this still select previous completion. + """ + event.current_buffer.auto_up() + + @kbmanager.registry.add_binding(Keys.ControlN, filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER))) + def _next_history_or_next_completion(event): + """ + Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit. + + If completer is open this still select next completion. + """ + event.current_buffer.auto_down() + + @kbmanager.registry.add_binding(Keys.ControlG, filter=( + HasFocus(DEFAULT_BUFFER) & HasCompletions() + )) + def _dismiss_completion(event): + b = event.current_buffer + if b.complete_state: + b.cancel_completion() + + @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER)) + def _reset_buffer(event): + b = event.current_buffer + if b.complete_state: + b.cancel_completion() + else: + b.reset() + + @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER)) + def _reset_search_buffer(event): + if event.current_buffer.document.text: + event.current_buffer.reset() + else: + event.cli.push_focus(DEFAULT_BUFFER) + + supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP')) + + @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend) + def _suspend_to_bg(event): + event.cli.suspend_to_background() + + @Condition + def cursor_in_leading_ws(cli): + before = cli.application.buffer.document.current_line_before_cursor + return (not before) or before.isspace() + + # Ctrl+I == Tab + @kbmanager.registry.add_binding(Keys.ControlI, + filter=(HasFocus(DEFAULT_BUFFER) + & ~HasSelection() + & insert_mode + & cursor_in_leading_ws + )) + def _indent_buffer(event): + event.current_buffer.insert_text(' ' * 4) + + if sys.platform == 'win32': + from IPython.lib.clipboard import (ClipboardEmpty, + win32_clipboard_get, tkinter_clipboard_get) + @kbmanager.registry.add_binding(Keys.ControlV, + filter=(HasFocus(DEFAULT_BUFFER) & ~ViMode())) + def _paste(event): + try: + text = win32_clipboard_get() + except TryNext: + try: + text = tkinter_clipboard_get() + except (TryNext, ClipboardEmpty): + return + except ClipboardEmpty: + return + event.current_buffer.insert_text(text.replace('\t', ' ' * 4)) + + # Pre-populate history from IPython's history database + history = InMemoryHistory() + last_cell = u"" + for __, ___, cell in self.history_manager.get_tail(self.history_load_length, + include_latest=True): + # Ignore blank lines and consecutive duplicates + cell = cell.rstrip() + if cell and (cell != last_cell): + history.append(cell) + + self._style = self._make_style_from_name(self.highlighting_style) + style = DynamicStyle(lambda: self._style) + + editing_mode = getattr(EditingMode, self.editing_mode.upper()) + + self._app = create_prompt_application( + editing_mode=editing_mode, + key_bindings_registry=kbmanager.registry, + history=history, + completer=IPythonPTCompleter(self.Completer), + enable_history_search=True, + style=style, + mouse_support=self.mouse_support, + **self._layout_options() + ) + self._eventloop = create_eventloop(self.inputhook) + self.pt_cli = CommandLineInterface(self._app, eventloop=self._eventloop) + + def _make_style_from_name(self, name): + """ + Small wrapper that make an IPython compatible style from a style name + + We need that to add style for prompt ... etc. + """ + style_cls = get_style_by_name(name) + style_overrides = { + Token.Prompt: '#009900', + Token.PromptNum: '#00ff00 bold', + Token.OutPrompt: '#990000', + Token.OutPromptNum: '#ff0000 bold', + } + if name == 'default': + style_cls = get_style_by_name('default') + # The default theme needs to be visible on both a dark background + # and a light background, because we can't tell what the terminal + # looks like. These tweaks to the default theme help with that. + style_overrides.update({ + Token.Number: '#007700', + Token.Operator: 'noinherit', + Token.String: '#BB6622', + Token.Name.Function: '#2080D0', + Token.Name.Class: 'bold #2080D0', + Token.Name.Namespace: 'bold #2080D0', + }) + style_overrides.update(self.highlighting_style_overrides) + style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls, + style_dict=style_overrides) + + return style + + def _layout_options(self): + """ + Return the current layout option for the current Terminal InteractiveShell + """ + return { + 'lexer':IPythonPTLexer(), + 'reserve_space_for_menu':self.space_for_menu, + 'get_prompt_tokens':self.prompts.in_prompt_tokens, + 'get_continuation_tokens':self.prompts.continuation_prompt_tokens, + 'multiline':True, + 'display_completions_in_columns': self.display_completions_in_columns, + + # Highlight matching brackets, but only when this setting is + # enabled, and only when the DEFAULT_BUFFER has the focus. + 'extra_input_processors': [ConditionalProcessor( + processor=HighlightMatchingBracketProcessor(chars='[](){}'), + filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() & + Condition(lambda cli: self.highlight_matching_brackets))], + } + + def _update_layout(self): + """ + Ask for a re computation of the application layout, if for example , + some configuration options have changed. + """ + if getattr(self, '._app', None): + self._app.layout = create_prompt_layout(**self._layout_options()) + + def prompt_for_code(self): + document = self.pt_cli.run( + pre_run=self.pre_prompt, reset_current_buffer=True) + return document.text + + def init_io(self): + if sys.platform not in {'win32', 'cli'}: + return + + import win_unicode_console + import colorama + + win_unicode_console.enable() + colorama.init() + + # For some reason we make these wrappers around stdout/stderr. + # For now, we need to reset them so all output gets coloured. + # https://github.com/ipython/ipython/issues/8669 + from IPython.utils import io + io.stdout = io.IOStream(sys.stdout) + io.stderr = io.IOStream(sys.stderr) + + def init_magics(self): + super(TerminalInteractiveShell, self).init_magics() + self.register_magics(TerminalMagics) + + def init_alias(self): + # The parent class defines aliases that can be safely used with any + # frontend. + super(TerminalInteractiveShell, self).init_alias() + + # Now define aliases that only make sense on the terminal, because they + # need direct access to the console in a way that we can't emulate in + # GUI or web frontend + if os.name == 'posix': + for cmd in ['clear', 'more', 'less', 'man']: + self.alias_manager.soft_define_alias(cmd, cmd) -warn("Since IPython 5.0 `IPython.terminal.interactiveshell` is deprecated in favor of `IPython.terminal.ptshell`.", - DeprecationWarning) -@undoc -class TerminalInteractiveShell(PromptToolkitShell): def __init__(self, *args, **kwargs): - warn("Since IPython 5.0 this is a deprecated alias for IPython.terminal.ptshell.TerminalInteractiveShell. " - "The terminal interface of this class now uses prompt_toolkit instead of readline.", - DeprecationWarning, stacklevel=2) - PromptToolkitShell.__init__(self, *args, **kwargs) + super(TerminalInteractiveShell, self).__init__(*args, **kwargs) + self.init_prompt_toolkit_cli() + self.init_term_title() + self.keep_running = True + + self.debugger_history = InMemoryHistory() + + def ask_exit(self): + self.keep_running = False + + rl_next_input = None + + def pre_prompt(self): + if self.rl_next_input: + self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input) + self.rl_next_input = None + + def interact(self): + while self.keep_running: + print(self.separate_in, end='') + + try: + code = self.prompt_for_code() + except EOFError: + if (not self.confirm_exit) \ + or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'): + self.ask_exit() + + else: + if code: + self.run_cell(code, store_history=True) + if self.autoedit_syntax and self.SyntaxTB.last_syntax_error: + self.edit_syntax_error() + + def mainloop(self): + # An extra layer of protection in case someone mashing Ctrl-C breaks + # out of our internal code. + while True: + try: + self.interact() + break + except KeyboardInterrupt: + print("\nKeyboardInterrupt escaped interact()\n") + + if hasattr(self, '_eventloop'): + self._eventloop.close() + + _inputhook = None + def inputhook(self, context): + if self._inputhook is not None: + self._inputhook(context) + + def enable_gui(self, gui=None): + if gui: + self._inputhook = get_inputhook_func(gui) + else: + self._inputhook = None + + # Methods to support auto-editing of SyntaxErrors: + + def edit_syntax_error(self): + """The bottom half of the syntax error handler called in the main loop. + + Loop until syntax error is fixed or user cancels. + """ + + while self.SyntaxTB.last_syntax_error: + # copy and clear last_syntax_error + err = self.SyntaxTB.clear_err_state() + if not self._should_recompile(err): + return + try: + # may set last_syntax_error again if a SyntaxError is raised + self.safe_execfile(err.filename, self.user_ns) + except: + self.showtraceback() + else: + try: + with open(err.filename) as f: + # This should be inside a display_trap block and I + # think it is. + sys.displayhook(f.read()) + except: + self.showtraceback() + + def _should_recompile(self, e): + """Utility routine for edit_syntax_error""" + + if e.filename in ('', '', '', + '', '', + None): + return False + try: + if (self.autoedit_syntax and + not self.ask_yes_no( + 'Return to editor to correct syntax error? ' + '[Y/n] ', 'y')): + return False + except EOFError: + return False + + def int0(x): + try: + return int(x) + except TypeError: + return 0 + + # always pass integer line and offset values to editor hook + try: + self.hooks.fix_error_editor(e.filename, + int0(e.lineno), int0(e.offset), + e.msg) + except TryNext: + warn('Could not open editor') + return False + return True + + # Run !system commands directly, not through pipes, so terminal programs + # work correctly. + system = InteractiveShell.system_raw + + def auto_rewrite_input(self, cmd): + """Overridden from the parent class to use fancy rewriting prompt""" + if not self.show_rewritten_input: + return + + tokens = self.prompts.rewrite_prompt_tokens() + if self.pt_cli: + self.pt_cli.print_tokens(tokens) + print(cmd) + else: + prompt = ''.join(s for t, s in tokens) + print(prompt, cmd, sep='') + + _prompts_before = None + def switch_doctest_mode(self, mode): + """Switch prompts to classic for %doctest_mode""" + if mode: + self._prompts_before = self.prompts + self.prompts = ClassicPrompts(self) + elif self._prompts_before: + self.prompts = self._prompts_before + self._prompts_before = None + + +InteractiveShellABC.register(TerminalInteractiveShell) + +if __name__ == '__main__': + TerminalInteractiveShell.instance().interact() diff --git a/IPython/terminal/ipapp.py b/IPython/terminal/ipapp.py index 1d89a18..8add461 100755 --- a/IPython/terminal/ipapp.py +++ b/IPython/terminal/ipapp.py @@ -32,7 +32,7 @@ from IPython.core.shellapp import ( InteractiveShellApp, shell_flags, shell_aliases ) from IPython.extensions.storemagic import StoreMagics -from .ptshell import TerminalInteractiveShell +from .interactiveshell import TerminalInteractiveShell from IPython.paths import get_ipython_dir from traitlets import ( Bool, List, Dict, default, observe, diff --git a/IPython/terminal/ptshell.py b/IPython/terminal/ptshell.py deleted file mode 100644 index 85c3a89..0000000 --- a/IPython/terminal/ptshell.py +++ /dev/null @@ -1,569 +0,0 @@ -"""IPython terminal interface using prompt_toolkit in place of readline""" -from __future__ import print_function - -import os -import sys -import signal -from warnings import warn - -from IPython.core.error import TryNext -from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC -from IPython.utils.py3compat import PY3, cast_unicode_py2, input -from IPython.utils.terminal import toggle_set_term_title, set_term_title -from IPython.utils.process import abbrev_cwd -from traitlets import Bool, Unicode, Dict, Integer, observe, Instance, Type, default - -from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER, EditingMode -from prompt_toolkit.filters import (HasFocus, HasSelection, Condition, - ViInsertMode, EmacsInsertMode, IsDone, HasCompletions) -from prompt_toolkit.filters.cli import ViMode -from prompt_toolkit.history import InMemoryHistory -from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout -from prompt_toolkit.interface import CommandLineInterface -from prompt_toolkit.key_binding.manager import KeyBindingManager -from prompt_toolkit.keys import Keys -from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor -from prompt_toolkit.styles import PygmentsStyle, DynamicStyle - -from pygments.styles import get_style_by_name, get_all_styles -from pygments.token import Token - -from .debugger import TerminalPdb, Pdb -from .magics import TerminalMagics -from .pt_inputhooks import get_inputhook_func -from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook -from .ptutils import IPythonPTCompleter, IPythonPTLexer - - -def get_default_editor(): - try: - ed = os.environ['EDITOR'] - if not PY3: - ed = ed.decode() - return ed - except KeyError: - pass - except UnicodeError: - warn("$EDITOR environment variable is not pure ASCII. Using platform " - "default editor.") - - if os.name == 'posix': - return 'vi' # the only one guaranteed to be there! - else: - return 'notepad' # same in Windows! - - -if sys.stdin and sys.stdout and sys.stderr: - _is_tty = (sys.stdin.isatty()) and (sys.stdout.isatty()) and (sys.stderr.isatty()) -else: - _is_tty = False - - -_use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty) - -class TerminalInteractiveShell(InteractiveShell): - colors_force = True - - space_for_menu = Integer(6, help='Number of line at the bottom of the screen ' - 'to reserve for the completion menu' - ).tag(config=True) - - def _space_for_menu_changed(self, old, new): - self._update_layout() - - pt_cli = None - debugger_history = None - - simple_prompt = Bool(_use_simple_prompt, - help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors. - - Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are: - IPython own testing machinery, and emacs inferior-shell integration through elpy. - - This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT` - environment variable is set, or the current terminal is not a tty. - - """ - ).tag(config=True) - - @property - def debugger_cls(self): - return Pdb if self.simple_prompt else TerminalPdb - - autoedit_syntax = Bool(False, - help="auto editing of files with syntax errors.", - ).tag(config=True) - - - confirm_exit = Bool(True, - help=""" - Set to confirm when you try to exit IPython with an EOF (Control-D - in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit', - you can force a direct exit without any confirmation.""", - ).tag(config=True) - - editing_mode = Unicode('emacs', - help="Shortcut style to use at the prompt. 'vi' or 'emacs'.", - ).tag(config=True) - - mouse_support = Bool(False, - help="Enable mouse support in the prompt" - ).tag(config=True) - - highlighting_style = Unicode('default', - help="The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles()) - ).tag(config=True) - - - @observe('highlighting_style') - def _highlighting_style_changed(self, change): - self._style = self._make_style_from_name(self.highlighting_style) - - highlighting_style_overrides = Dict( - help="Override highlighting format for specific tokens" - ).tag(config=True) - - editor = Unicode(get_default_editor(), - help="Set the editor used by IPython (default to $EDITOR/vi/notepad)." - ).tag(config=True) - - prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True) - - prompts = Instance(Prompts) - - @default('prompts') - def _prompts_default(self): - return self.prompts_class(self) - - @observe('prompts') - def _(self, change): - self._update_layout() - - @default('displayhook_class') - def _displayhook_class_default(self): - return RichPromptDisplayHook - - term_title = Bool(True, - help="Automatically set the terminal title" - ).tag(config=True) - - display_completions_in_columns = Bool(False, - help="Display a multi column completion menu.", - ).tag(config=True) - - highlight_matching_brackets = Bool(True, - help="Highlight matching brackets .", - ).tag(config=True) - - @observe('term_title') - def init_term_title(self, change=None): - # Enable or disable the terminal title. - if self.term_title: - toggle_set_term_title(True) - set_term_title('IPython: ' + abbrev_cwd()) - else: - toggle_set_term_title(False) - - def init_display_formatter(self): - super(TerminalInteractiveShell, self).init_display_formatter() - # terminal only supports plain text - self.display_formatter.active_types = ['text/plain'] - - def init_prompt_toolkit_cli(self): - self._app = None - if self.simple_prompt: - # Fall back to plain non-interactive output for tests. - # This is very limited, and only accepts a single line. - def prompt(): - return cast_unicode_py2(input('In [%d]: ' % self.execution_count)) - self.prompt_for_code = prompt - return - - kbmanager = KeyBindingManager.for_prompt() - insert_mode = ViInsertMode() | EmacsInsertMode() - # Ctrl+J == Enter, seemingly - @kbmanager.registry.add_binding(Keys.ControlJ, - filter=(HasFocus(DEFAULT_BUFFER) - & ~HasSelection() - & insert_mode - )) - def _(event): - b = event.current_buffer - d = b.document - - if b.complete_state: - cc = b.complete_state.current_completion - if cc: - b.apply_completion(cc) - else: - b.cancel_completion() - return - - if not (d.on_last_line or d.cursor_position_row >= d.line_count - - d.empty_line_count_at_the_end()): - b.newline() - return - - status, indent = self.input_splitter.check_complete(d.text + '\n') - - if (status != 'incomplete') and b.accept_action.is_returnable: - b.accept_action.validate_and_handle(event.cli, b) - else: - b.insert_text('\n' + (' ' * (indent or 0))) - - @kbmanager.registry.add_binding(Keys.ControlP, filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER))) - def _previous_history_or_previous_completion(event): - """ - Control-P in vi edit mode on readline is history next, unlike default prompt toolkit. - - If completer is open this still select previous completion. - """ - event.current_buffer.auto_up() - - @kbmanager.registry.add_binding(Keys.ControlN, filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER))) - def _next_history_or_next_completion(event): - """ - Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit. - - If completer is open this still select next completion. - """ - event.current_buffer.auto_down() - - @kbmanager.registry.add_binding(Keys.ControlG, filter=( - HasFocus(DEFAULT_BUFFER) & HasCompletions() - )) - def _dismiss_completion(event): - b = event.current_buffer - if b.complete_state: - b.cancel_completion() - - @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER)) - def _reset_buffer(event): - b = event.current_buffer - if b.complete_state: - b.cancel_completion() - else: - b.reset() - - @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER)) - def _reset_search_buffer(event): - if event.current_buffer.document.text: - event.current_buffer.reset() - else: - event.cli.push_focus(DEFAULT_BUFFER) - - supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP')) - - @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend) - def _suspend_to_bg(event): - event.cli.suspend_to_background() - - @Condition - def cursor_in_leading_ws(cli): - before = cli.application.buffer.document.current_line_before_cursor - return (not before) or before.isspace() - - # Ctrl+I == Tab - @kbmanager.registry.add_binding(Keys.ControlI, - filter=(HasFocus(DEFAULT_BUFFER) - & ~HasSelection() - & insert_mode - & cursor_in_leading_ws - )) - def _indent_buffer(event): - event.current_buffer.insert_text(' ' * 4) - - if sys.platform == 'win32': - from IPython.lib.clipboard import (ClipboardEmpty, - win32_clipboard_get, tkinter_clipboard_get) - @kbmanager.registry.add_binding(Keys.ControlV, - filter=(HasFocus(DEFAULT_BUFFER) & ~ViMode())) - def _paste(event): - try: - text = win32_clipboard_get() - except TryNext: - try: - text = tkinter_clipboard_get() - except (TryNext, ClipboardEmpty): - return - except ClipboardEmpty: - return - event.current_buffer.insert_text(text.replace('\t', ' ' * 4)) - - # Pre-populate history from IPython's history database - history = InMemoryHistory() - last_cell = u"" - for __, ___, cell in self.history_manager.get_tail(self.history_load_length, - include_latest=True): - # Ignore blank lines and consecutive duplicates - cell = cell.rstrip() - if cell and (cell != last_cell): - history.append(cell) - - self._style = self._make_style_from_name(self.highlighting_style) - style = DynamicStyle(lambda: self._style) - - editing_mode = getattr(EditingMode, self.editing_mode.upper()) - - self._app = create_prompt_application( - editing_mode=editing_mode, - key_bindings_registry=kbmanager.registry, - history=history, - completer=IPythonPTCompleter(self.Completer), - enable_history_search=True, - style=style, - mouse_support=self.mouse_support, - **self._layout_options() - ) - self._eventloop = create_eventloop(self.inputhook) - self.pt_cli = CommandLineInterface(self._app, eventloop=self._eventloop) - - def _make_style_from_name(self, name): - """ - Small wrapper that make an IPython compatible style from a style name - - We need that to add style for prompt ... etc. - """ - style_cls = get_style_by_name(name) - style_overrides = { - Token.Prompt: '#009900', - Token.PromptNum: '#00ff00 bold', - Token.OutPrompt: '#990000', - Token.OutPromptNum: '#ff0000 bold', - } - if name == 'default': - style_cls = get_style_by_name('default') - # The default theme needs to be visible on both a dark background - # and a light background, because we can't tell what the terminal - # looks like. These tweaks to the default theme help with that. - style_overrides.update({ - Token.Number: '#007700', - Token.Operator: 'noinherit', - Token.String: '#BB6622', - Token.Name.Function: '#2080D0', - Token.Name.Class: 'bold #2080D0', - Token.Name.Namespace: 'bold #2080D0', - }) - style_overrides.update(self.highlighting_style_overrides) - style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls, - style_dict=style_overrides) - - return style - - def _layout_options(self): - """ - Return the current layout option for the current Terminal InteractiveShell - """ - return { - 'lexer':IPythonPTLexer(), - 'reserve_space_for_menu':self.space_for_menu, - 'get_prompt_tokens':self.prompts.in_prompt_tokens, - 'get_continuation_tokens':self.prompts.continuation_prompt_tokens, - 'multiline':True, - 'display_completions_in_columns': self.display_completions_in_columns, - - # Highlight matching brackets, but only when this setting is - # enabled, and only when the DEFAULT_BUFFER has the focus. - 'extra_input_processors': [ConditionalProcessor( - processor=HighlightMatchingBracketProcessor(chars='[](){}'), - filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() & - Condition(lambda cli: self.highlight_matching_brackets))], - } - - def _update_layout(self): - """ - Ask for a re computation of the application layout, if for example , - some configuration options have changed. - """ - if getattr(self, '._app', None): - self._app.layout = create_prompt_layout(**self._layout_options()) - - def prompt_for_code(self): - document = self.pt_cli.run( - pre_run=self.pre_prompt, reset_current_buffer=True) - return document.text - - def init_io(self): - if sys.platform not in {'win32', 'cli'}: - return - - import win_unicode_console - import colorama - - win_unicode_console.enable() - colorama.init() - - # For some reason we make these wrappers around stdout/stderr. - # For now, we need to reset them so all output gets coloured. - # https://github.com/ipython/ipython/issues/8669 - from IPython.utils import io - io.stdout = io.IOStream(sys.stdout) - io.stderr = io.IOStream(sys.stderr) - - def init_magics(self): - super(TerminalInteractiveShell, self).init_magics() - self.register_magics(TerminalMagics) - - def init_alias(self): - # The parent class defines aliases that can be safely used with any - # frontend. - super(TerminalInteractiveShell, self).init_alias() - - # Now define aliases that only make sense on the terminal, because they - # need direct access to the console in a way that we can't emulate in - # GUI or web frontend - if os.name == 'posix': - for cmd in ['clear', 'more', 'less', 'man']: - self.alias_manager.soft_define_alias(cmd, cmd) - - - def __init__(self, *args, **kwargs): - super(TerminalInteractiveShell, self).__init__(*args, **kwargs) - self.init_prompt_toolkit_cli() - self.init_term_title() - self.keep_running = True - - self.debugger_history = InMemoryHistory() - - def ask_exit(self): - self.keep_running = False - - rl_next_input = None - - def pre_prompt(self): - if self.rl_next_input: - self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input) - self.rl_next_input = None - - def interact(self): - while self.keep_running: - print(self.separate_in, end='') - - try: - code = self.prompt_for_code() - except EOFError: - if (not self.confirm_exit) \ - or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'): - self.ask_exit() - - else: - if code: - self.run_cell(code, store_history=True) - if self.autoedit_syntax and self.SyntaxTB.last_syntax_error: - self.edit_syntax_error() - - def mainloop(self): - # An extra layer of protection in case someone mashing Ctrl-C breaks - # out of our internal code. - while True: - try: - self.interact() - break - except KeyboardInterrupt: - print("\nKeyboardInterrupt escaped interact()\n") - - if hasattr(self, '_eventloop'): - self._eventloop.close() - - _inputhook = None - def inputhook(self, context): - if self._inputhook is not None: - self._inputhook(context) - - def enable_gui(self, gui=None): - if gui: - self._inputhook = get_inputhook_func(gui) - else: - self._inputhook = None - - # Methods to support auto-editing of SyntaxErrors: - - def edit_syntax_error(self): - """The bottom half of the syntax error handler called in the main loop. - - Loop until syntax error is fixed or user cancels. - """ - - while self.SyntaxTB.last_syntax_error: - # copy and clear last_syntax_error - err = self.SyntaxTB.clear_err_state() - if not self._should_recompile(err): - return - try: - # may set last_syntax_error again if a SyntaxError is raised - self.safe_execfile(err.filename, self.user_ns) - except: - self.showtraceback() - else: - try: - with open(err.filename) as f: - # This should be inside a display_trap block and I - # think it is. - sys.displayhook(f.read()) - except: - self.showtraceback() - - def _should_recompile(self, e): - """Utility routine for edit_syntax_error""" - - if e.filename in ('', '', '', - '', '', - None): - return False - try: - if (self.autoedit_syntax and - not self.ask_yes_no( - 'Return to editor to correct syntax error? ' - '[Y/n] ', 'y')): - return False - except EOFError: - return False - - def int0(x): - try: - return int(x) - except TypeError: - return 0 - - # always pass integer line and offset values to editor hook - try: - self.hooks.fix_error_editor(e.filename, - int0(e.lineno), int0(e.offset), - e.msg) - except TryNext: - warn('Could not open editor') - return False - return True - - # Run !system commands directly, not through pipes, so terminal programs - # work correctly. - system = InteractiveShell.system_raw - - def auto_rewrite_input(self, cmd): - """Overridden from the parent class to use fancy rewriting prompt""" - if not self.show_rewritten_input: - return - - tokens = self.prompts.rewrite_prompt_tokens() - if self.pt_cli: - self.pt_cli.print_tokens(tokens) - print(cmd) - else: - prompt = ''.join(s for t, s in tokens) - print(prompt, cmd, sep='') - - _prompts_before = None - def switch_doctest_mode(self, mode): - """Switch prompts to classic for %doctest_mode""" - if mode: - self._prompts_before = self.prompts - self.prompts = ClassicPrompts(self) - elif self._prompts_before: - self.prompts = self._prompts_before - self._prompts_before = None - - -InteractiveShellABC.register(TerminalInteractiveShell) - -if __name__ == '__main__': - TerminalInteractiveShell.instance().interact()