##// END OF EJS Templates
Merge pull request #9626 from Carreau/undeprecate-interactiveshell...
Thomas Kluyver -
r22558:bdfd4e1f merge
parent child Browse files
Show More
@@ -365,22 +365,22 b' class InteractiveShell(SingletonConfigurable):'
365 365 # deprecated prompt traits:
366 366
367 367 prompt_in1 = Unicode('In [\\#]: ',
368 help="Deprecated since IPython 4.0 and ignored since 5.0, set IPython.terminal.ptshell.TerminalInteractiveShell.prompts object directly."
368 help="Deprecated since IPython 4.0 and ignored since 5.0, set TerminalInteractiveShell.prompts object directly."
369 369 ).tag(config=True)
370 370 prompt_in2 = Unicode(' .\\D.: ',
371 help="Deprecated since IPython 4.0 and ignored since 5.0, set IPython.terminal.ptshell.TerminalInteractiveShell.prompts object directly."
371 help="Deprecated since IPython 4.0 and ignored since 5.0, set TerminalInteractiveShell.prompts object directly."
372 372 ).tag(config=True)
373 373 prompt_out = Unicode('Out[\\#]: ',
374 help="Deprecated since IPython 4.0 and ignored since 5.0, set IPython.terminal.ptshell.TerminalInteractiveShell.prompts object directly."
374 help="Deprecated since IPython 4.0 and ignored since 5.0, set TerminalInteractiveShell.prompts object directly."
375 375 ).tag(config=True)
376 376 prompts_pad_left = Bool(True,
377 help="Deprecated since IPython 4.0 and ignored since 5.0, set IPython.terminal.ptshell.TerminalInteractiveShell.prompts object directly."
377 help="Deprecated since IPython 4.0 and ignored since 5.0, set TerminalInteractiveShell.prompts object directly."
378 378 ).tag(config=True)
379 379
380 380 @observe('prompt_in1', 'prompt_in2', 'prompt_out', 'prompt_pad_left')
381 381 def _prompt_trait_changed(self, change):
382 382 name = change['name']
383 warn("InteractiveShell.{name} is deprecated since IPython 4.0 and ignored since 5.0, set IPython.terminal.ptshell.TerminalInteractiveShell.prompts object directly.".format(
383 warn("InteractiveShell.{name} is deprecated since IPython 4.0 and ignored since 5.0, set TerminalInteractiveShell.prompts object directly.".format(
384 384 name=name)
385 385 )
386 386 # protect against weird cases where self.config may not exist:
@@ -13,9 +13,8 b' import warnings'
13 13
14 14 from IPython.core import ultratb, compilerop
15 15 from IPython.core.magic import Magics, magics_class, line_magic
16 from IPython.core.interactiveshell import DummyMod
17 from IPython.core.interactiveshell import InteractiveShell
18 from IPython.terminal.ptshell import TerminalInteractiveShell
16 from IPython.core.interactiveshell import DummyMod, InteractiveShell
17 from IPython.terminal.interactiveshell import TerminalInteractiveShell
19 18 from IPython.terminal.ipapp import load_default_config
20 19
21 20 from traitlets import Bool, CBool, Unicode
This diff has been collapsed as it changes many lines, (598 lines changed) Show them Hide them
@@ -1,21 +1,589 b''
1 # -*- coding: utf-8 -*-
2 """DEPRECATED: old import location of TerminalInteractiveShell"""
3
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
1 """IPython terminal interface using prompt_toolkit"""
2 from __future__ import print_function
6 3
4 import os
5 import sys
6 import signal
7 7 from warnings import warn
8 8
9 from IPython.utils.decorators import undoc
10 from .ptshell import TerminalInteractiveShell as PromptToolkitShell
9 from IPython.core.error import TryNext
10 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
11 from IPython.utils.py3compat import PY3, cast_unicode_py2, input
12 from IPython.utils.terminal import toggle_set_term_title, set_term_title
13 from IPython.utils.process import abbrev_cwd
14 from traitlets import Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum
15
16 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER, EditingMode
17 from prompt_toolkit.filters import (HasFocus, HasSelection, Condition,
18 ViInsertMode, EmacsInsertMode, IsDone, HasCompletions)
19 from prompt_toolkit.filters.cli import ViMode
20 from prompt_toolkit.history import InMemoryHistory
21 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout
22 from prompt_toolkit.interface import CommandLineInterface
23 from prompt_toolkit.key_binding.manager import KeyBindingManager
24 from prompt_toolkit.keys import Keys
25 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
26 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
27 from prompt_toolkit.key_binding.bindings.completion import display_completions_like_readline
28
29 from pygments.styles import get_style_by_name, get_all_styles
30 from pygments.token import Token
31
32 from .debugger import TerminalPdb, Pdb
33 from .magics import TerminalMagics
34 from .pt_inputhooks import get_inputhook_func
35 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
36 from .ptutils import IPythonPTCompleter, IPythonPTLexer
37
38
39 def get_default_editor():
40 try:
41 ed = os.environ['EDITOR']
42 if not PY3:
43 ed = ed.decode()
44 return ed
45 except KeyError:
46 pass
47 except UnicodeError:
48 warn("$EDITOR environment variable is not pure ASCII. Using platform "
49 "default editor.")
50
51 if os.name == 'posix':
52 return 'vi' # the only one guaranteed to be there!
53 else:
54 return 'notepad' # same in Windows!
55
56
57 if sys.stdin and sys.stdout and sys.stderr:
58 _is_tty = (sys.stdin.isatty()) and (sys.stdout.isatty()) and (sys.stderr.isatty())
59 else:
60 _is_tty = False
61
62
63 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
64
65 class TerminalInteractiveShell(InteractiveShell):
66 colors_force = True
67
68 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
69 'to reserve for the completion menu'
70 ).tag(config=True)
71
72 def _space_for_menu_changed(self, old, new):
73 self._update_layout()
74
75 pt_cli = None
76 debugger_history = None
77
78 simple_prompt = Bool(_use_simple_prompt,
79 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
80
81 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
82 IPython own testing machinery, and emacs inferior-shell integration through elpy.
83
84 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
85 environment variable is set, or the current terminal is not a tty.
86
87 """
88 ).tag(config=True)
89
90 @property
91 def debugger_cls(self):
92 return Pdb if self.simple_prompt else TerminalPdb
93
94 autoedit_syntax = Bool(False,
95 help="auto editing of files with syntax errors.",
96 ).tag(config=True)
97
98
99 confirm_exit = Bool(True,
100 help="""
101 Set to confirm when you try to exit IPython with an EOF (Control-D
102 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
103 you can force a direct exit without any confirmation.""",
104 ).tag(config=True)
105
106 editing_mode = Unicode('emacs',
107 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
108 ).tag(config=True)
109
110 mouse_support = Bool(False,
111 help="Enable mouse support in the prompt"
112 ).tag(config=True)
113
114 highlighting_style = Unicode('default',
115 help="The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles())
116 ).tag(config=True)
117
118
119 @observe('highlighting_style')
120 def _highlighting_style_changed(self, change):
121 self._style = self._make_style_from_name(self.highlighting_style)
122
123 highlighting_style_overrides = Dict(
124 help="Override highlighting format for specific tokens"
125 ).tag(config=True)
126
127 editor = Unicode(get_default_editor(),
128 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
129 ).tag(config=True)
130
131 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
132
133 prompts = Instance(Prompts)
134
135 @default('prompts')
136 def _prompts_default(self):
137 return self.prompts_class(self)
138
139 @observe('prompts')
140 def _(self, change):
141 self._update_layout()
142
143 @default('displayhook_class')
144 def _displayhook_class_default(self):
145 return RichPromptDisplayHook
146
147 term_title = Bool(True,
148 help="Automatically set the terminal title"
149 ).tag(config=True)
150
151 display_completions_in_columns = Bool(None,
152 help="Display a multi column completion menu.", allow_none=True
153 ).tag(config=True)
154
155 @observe('display_completions_in_columns')
156 def _display_completions_in_columns_changed(self, new):
157 raise DeprecationWarning("The `display_completions_in_columns` Boolean has been replaced by the enum `display_completions`"
158 "with the following acceptable value: 'column', 'multicolumn','readlinelike'. ")
159
160 display_completions = Enum(('column', 'multicolumn','readlinelike'), default_value='multicolumn').tag(config=True)
161
162 highlight_matching_brackets = Bool(True,
163 help="Highlight matching brackets .",
164 ).tag(config=True)
165
166 @observe('term_title')
167 def init_term_title(self, change=None):
168 # Enable or disable the terminal title.
169 if self.term_title:
170 toggle_set_term_title(True)
171 set_term_title('IPython: ' + abbrev_cwd())
172 else:
173 toggle_set_term_title(False)
174
175 def init_display_formatter(self):
176 super(TerminalInteractiveShell, self).init_display_formatter()
177 # terminal only supports plain text
178 self.display_formatter.active_types = ['text/plain']
179
180 def init_prompt_toolkit_cli(self):
181 self._app = None
182 if self.simple_prompt:
183 # Fall back to plain non-interactive output for tests.
184 # This is very limited, and only accepts a single line.
185 def prompt():
186 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
187 self.prompt_for_code = prompt
188 return
189
190 kbmanager = KeyBindingManager.for_prompt()
191 insert_mode = ViInsertMode() | EmacsInsertMode()
192 # Ctrl+J == Enter, seemingly
193 @kbmanager.registry.add_binding(Keys.ControlJ,
194 filter=(HasFocus(DEFAULT_BUFFER)
195 & ~HasSelection()
196 & insert_mode
197 ))
198 def _(event):
199 b = event.current_buffer
200 d = b.document
201
202 if b.complete_state:
203 cc = b.complete_state.current_completion
204 if cc:
205 b.apply_completion(cc)
206 else:
207 b.cancel_completion()
208 return
209
210 if not (d.on_last_line or d.cursor_position_row >= d.line_count
211 - d.empty_line_count_at_the_end()):
212 b.newline()
213 return
214
215 status, indent = self.input_splitter.check_complete(d.text + '\n')
216
217 if (status != 'incomplete') and b.accept_action.is_returnable:
218 b.accept_action.validate_and_handle(event.cli, b)
219 else:
220 b.insert_text('\n' + (' ' * (indent or 0)))
221
222 @kbmanager.registry.add_binding(Keys.ControlP, filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER)))
223 def _previous_history_or_previous_completion(event):
224 """
225 Control-P in vi edit mode on readline is history next, unlike default prompt toolkit.
226
227 If completer is open this still select previous completion.
228 """
229 event.current_buffer.auto_up()
230
231 @kbmanager.registry.add_binding(Keys.ControlN, filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER)))
232 def _next_history_or_next_completion(event):
233 """
234 Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit.
235
236 If completer is open this still select next completion.
237 """
238 event.current_buffer.auto_down()
239
240 @kbmanager.registry.add_binding(Keys.ControlG, filter=(
241 HasFocus(DEFAULT_BUFFER) & HasCompletions()
242 ))
243 def _dismiss_completion(event):
244 b = event.current_buffer
245 if b.complete_state:
246 b.cancel_completion()
247
248 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER))
249 def _reset_buffer(event):
250 b = event.current_buffer
251 if b.complete_state:
252 b.cancel_completion()
253 else:
254 b.reset()
255
256 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER))
257 def _reset_search_buffer(event):
258 if event.current_buffer.document.text:
259 event.current_buffer.reset()
260 else:
261 event.cli.push_focus(DEFAULT_BUFFER)
262
263 supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP'))
264
265 @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend)
266 def _suspend_to_bg(event):
267 event.cli.suspend_to_background()
268
269 @Condition
270 def cursor_in_leading_ws(cli):
271 before = cli.application.buffer.document.current_line_before_cursor
272 return (not before) or before.isspace()
273
274 # Ctrl+I == Tab
275 @kbmanager.registry.add_binding(Keys.ControlI,
276 filter=(HasFocus(DEFAULT_BUFFER)
277 & ~HasSelection()
278 & insert_mode
279 & cursor_in_leading_ws
280 ))
281 def _indent_buffer(event):
282 event.current_buffer.insert_text(' ' * 4)
283
284
285 if self.display_completions == 'readlinelike':
286 @kbmanager.registry.add_binding(Keys.ControlI,
287 filter=(HasFocus(DEFAULT_BUFFER)
288 & ~HasSelection()
289 & insert_mode
290 & ~cursor_in_leading_ws
291 ))
292 def _disaply_compl(ev):
293 display_completions_like_readline(ev)
294
295
296 if sys.platform == 'win32':
297 from IPython.lib.clipboard import (ClipboardEmpty,
298 win32_clipboard_get, tkinter_clipboard_get)
299 @kbmanager.registry.add_binding(Keys.ControlV,
300 filter=(HasFocus(DEFAULT_BUFFER) & ~ViMode()))
301 def _paste(event):
302 try:
303 text = win32_clipboard_get()
304 except TryNext:
305 try:
306 text = tkinter_clipboard_get()
307 except (TryNext, ClipboardEmpty):
308 return
309 except ClipboardEmpty:
310 return
311 event.current_buffer.insert_text(text.replace('\t', ' ' * 4))
312
313 # Pre-populate history from IPython's history database
314 history = InMemoryHistory()
315 last_cell = u""
316 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
317 include_latest=True):
318 # Ignore blank lines and consecutive duplicates
319 cell = cell.rstrip()
320 if cell and (cell != last_cell):
321 history.append(cell)
322
323 self._style = self._make_style_from_name(self.highlighting_style)
324 style = DynamicStyle(lambda: self._style)
325
326 editing_mode = getattr(EditingMode, self.editing_mode.upper())
327
328 self._app = create_prompt_application(
329 editing_mode=editing_mode,
330 key_bindings_registry=kbmanager.registry,
331 history=history,
332 completer=IPythonPTCompleter(self.Completer),
333 enable_history_search=True,
334 style=style,
335 mouse_support=self.mouse_support,
336 **self._layout_options()
337 )
338 self._eventloop = create_eventloop(self.inputhook)
339 self.pt_cli = CommandLineInterface(self._app, eventloop=self._eventloop)
340
341 def _make_style_from_name(self, name):
342 """
343 Small wrapper that make an IPython compatible style from a style name
344
345 We need that to add style for prompt ... etc.
346 """
347 style_cls = get_style_by_name(name)
348 style_overrides = {
349 Token.Prompt: '#009900',
350 Token.PromptNum: '#00ff00 bold',
351 Token.OutPrompt: '#990000',
352 Token.OutPromptNum: '#ff0000 bold',
353 }
354 if name == 'default':
355 style_cls = get_style_by_name('default')
356 # The default theme needs to be visible on both a dark background
357 # and a light background, because we can't tell what the terminal
358 # looks like. These tweaks to the default theme help with that.
359 style_overrides.update({
360 Token.Number: '#007700',
361 Token.Operator: 'noinherit',
362 Token.String: '#BB6622',
363 Token.Name.Function: '#2080D0',
364 Token.Name.Class: 'bold #2080D0',
365 Token.Name.Namespace: 'bold #2080D0',
366 })
367 style_overrides.update(self.highlighting_style_overrides)
368 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
369 style_dict=style_overrides)
370
371 return style
372
373 def _layout_options(self):
374 """
375 Return the current layout option for the current Terminal InteractiveShell
376 """
377 return {
378 'lexer':IPythonPTLexer(),
379 'reserve_space_for_menu':self.space_for_menu,
380 'get_prompt_tokens':self.prompts.in_prompt_tokens,
381 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
382 'multiline':True,
383 'display_completions_in_columns': (self.display_completions == 'multicolumn'),
384
385 # Highlight matching brackets, but only when this setting is
386 # enabled, and only when the DEFAULT_BUFFER has the focus.
387 'extra_input_processors': [ConditionalProcessor(
388 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
389 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
390 Condition(lambda cli: self.highlight_matching_brackets))],
391 }
392
393 def _update_layout(self):
394 """
395 Ask for a re computation of the application layout, if for example ,
396 some configuration options have changed.
397 """
398 if getattr(self, '._app', None):
399 self._app.layout = create_prompt_layout(**self._layout_options())
400
401 def prompt_for_code(self):
402 document = self.pt_cli.run(
403 pre_run=self.pre_prompt, reset_current_buffer=True)
404 return document.text
405
406 def init_io(self):
407 if sys.platform not in {'win32', 'cli'}:
408 return
409
410 import win_unicode_console
411 import colorama
412
413 win_unicode_console.enable()
414 colorama.init()
415
416 # For some reason we make these wrappers around stdout/stderr.
417 # For now, we need to reset them so all output gets coloured.
418 # https://github.com/ipython/ipython/issues/8669
419 from IPython.utils import io
420 io.stdout = io.IOStream(sys.stdout)
421 io.stderr = io.IOStream(sys.stderr)
422
423 def init_magics(self):
424 super(TerminalInteractiveShell, self).init_magics()
425 self.register_magics(TerminalMagics)
426
427 def init_alias(self):
428 # The parent class defines aliases that can be safely used with any
429 # frontend.
430 super(TerminalInteractiveShell, self).init_alias()
431
432 # Now define aliases that only make sense on the terminal, because they
433 # need direct access to the console in a way that we can't emulate in
434 # GUI or web frontend
435 if os.name == 'posix':
436 for cmd in ['clear', 'more', 'less', 'man']:
437 self.alias_manager.soft_define_alias(cmd, cmd)
11 438
12 warn("Since IPython 5.0 `IPython.terminal.interactiveshell` is deprecated in favor of `IPython.terminal.ptshell`.",
13 DeprecationWarning)
14 439
15 @undoc
16 class TerminalInteractiveShell(PromptToolkitShell):
17 440 def __init__(self, *args, **kwargs):
18 warn("Since IPython 5.0 this is a deprecated alias for IPython.terminal.ptshell.TerminalInteractiveShell. "
19 "The terminal interface of this class now uses prompt_toolkit instead of readline.",
20 DeprecationWarning, stacklevel=2)
21 PromptToolkitShell.__init__(self, *args, **kwargs)
441 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
442 self.init_prompt_toolkit_cli()
443 self.init_term_title()
444 self.keep_running = True
445
446 self.debugger_history = InMemoryHistory()
447
448 def ask_exit(self):
449 self.keep_running = False
450
451 rl_next_input = None
452
453 def pre_prompt(self):
454 if self.rl_next_input:
455 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
456 self.rl_next_input = None
457
458 def interact(self):
459 while self.keep_running:
460 print(self.separate_in, end='')
461
462 try:
463 code = self.prompt_for_code()
464 except EOFError:
465 if (not self.confirm_exit) \
466 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
467 self.ask_exit()
468
469 else:
470 if code:
471 self.run_cell(code, store_history=True)
472 if self.autoedit_syntax and self.SyntaxTB.last_syntax_error:
473 self.edit_syntax_error()
474
475 def mainloop(self):
476 # An extra layer of protection in case someone mashing Ctrl-C breaks
477 # out of our internal code.
478 while True:
479 try:
480 self.interact()
481 break
482 except KeyboardInterrupt:
483 print("\nKeyboardInterrupt escaped interact()\n")
484
485 if hasattr(self, '_eventloop'):
486 self._eventloop.close()
487
488 _inputhook = None
489 def inputhook(self, context):
490 if self._inputhook is not None:
491 self._inputhook(context)
492
493 def enable_gui(self, gui=None):
494 if gui:
495 self._inputhook = get_inputhook_func(gui)
496 else:
497 self._inputhook = None
498
499 # Methods to support auto-editing of SyntaxErrors:
500
501 def edit_syntax_error(self):
502 """The bottom half of the syntax error handler called in the main loop.
503
504 Loop until syntax error is fixed or user cancels.
505 """
506
507 while self.SyntaxTB.last_syntax_error:
508 # copy and clear last_syntax_error
509 err = self.SyntaxTB.clear_err_state()
510 if not self._should_recompile(err):
511 return
512 try:
513 # may set last_syntax_error again if a SyntaxError is raised
514 self.safe_execfile(err.filename, self.user_ns)
515 except:
516 self.showtraceback()
517 else:
518 try:
519 with open(err.filename) as f:
520 # This should be inside a display_trap block and I
521 # think it is.
522 sys.displayhook(f.read())
523 except:
524 self.showtraceback()
525
526 def _should_recompile(self, e):
527 """Utility routine for edit_syntax_error"""
528
529 if e.filename in ('<ipython console>', '<input>', '<string>',
530 '<console>', '<BackgroundJob compilation>',
531 None):
532 return False
533 try:
534 if (self.autoedit_syntax and
535 not self.ask_yes_no(
536 'Return to editor to correct syntax error? '
537 '[Y/n] ', 'y')):
538 return False
539 except EOFError:
540 return False
541
542 def int0(x):
543 try:
544 return int(x)
545 except TypeError:
546 return 0
547
548 # always pass integer line and offset values to editor hook
549 try:
550 self.hooks.fix_error_editor(e.filename,
551 int0(e.lineno), int0(e.offset),
552 e.msg)
553 except TryNext:
554 warn('Could not open editor')
555 return False
556 return True
557
558 # Run !system commands directly, not through pipes, so terminal programs
559 # work correctly.
560 system = InteractiveShell.system_raw
561
562 def auto_rewrite_input(self, cmd):
563 """Overridden from the parent class to use fancy rewriting prompt"""
564 if not self.show_rewritten_input:
565 return
566
567 tokens = self.prompts.rewrite_prompt_tokens()
568 if self.pt_cli:
569 self.pt_cli.print_tokens(tokens)
570 print(cmd)
571 else:
572 prompt = ''.join(s for t, s in tokens)
573 print(prompt, cmd, sep='')
574
575 _prompts_before = None
576 def switch_doctest_mode(self, mode):
577 """Switch prompts to classic for %doctest_mode"""
578 if mode:
579 self._prompts_before = self.prompts
580 self.prompts = ClassicPrompts(self)
581 elif self._prompts_before:
582 self.prompts = self._prompts_before
583 self._prompts_before = None
584
585
586 InteractiveShellABC.register(TerminalInteractiveShell)
587
588 if __name__ == '__main__':
589 TerminalInteractiveShell.instance().interact()
@@ -32,7 +32,7 b' from IPython.core.shellapp import ('
32 32 InteractiveShellApp, shell_flags, shell_aliases
33 33 )
34 34 from IPython.extensions.storemagic import StoreMagics
35 from .ptshell import TerminalInteractiveShell
35 from .interactiveshell import TerminalInteractiveShell
36 36 from IPython.paths import get_ipython_dir
37 37 from traitlets import (
38 38 Bool, List, Dict, default, observe,
This diff has been collapsed as it changes many lines, (573 lines changed) Show them Hide them
@@ -1,569 +1,8 b''
1 """IPython terminal interface using prompt_toolkit in place of readline"""
2 from __future__ import print_function
1 raise DeprecationWarning("""DEPRECATED:
3 2
4 import os
5 import sys
6 import signal
7 from warnings import warn
3 After Popular request and decision from the BDFL:
4 `IPython.terminal.ptshell` has been moved back to `IPython.terminal.interactiveshell`
5 during the beta cycle (after IPython 5.0.beta3) Sorry about that.
8 6
9 from IPython.core.error import TryNext
10 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
11 from IPython.utils.py3compat import PY3, cast_unicode_py2, input
12 from IPython.utils.terminal import toggle_set_term_title, set_term_title
13 from IPython.utils.process import abbrev_cwd
14 from traitlets import Bool, Unicode, Dict, Integer, observe, Instance, Type, default
15
16 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER, EditingMode
17 from prompt_toolkit.filters import (HasFocus, HasSelection, Condition,
18 ViInsertMode, EmacsInsertMode, IsDone, HasCompletions)
19 from prompt_toolkit.filters.cli import ViMode
20 from prompt_toolkit.history import InMemoryHistory
21 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout
22 from prompt_toolkit.interface import CommandLineInterface
23 from prompt_toolkit.key_binding.manager import KeyBindingManager
24 from prompt_toolkit.keys import Keys
25 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
26 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
27
28 from pygments.styles import get_style_by_name, get_all_styles
29 from pygments.token import Token
30
31 from .debugger import TerminalPdb, Pdb
32 from .magics import TerminalMagics
33 from .pt_inputhooks import get_inputhook_func
34 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
35 from .ptutils import IPythonPTCompleter, IPythonPTLexer
36
37
38 def get_default_editor():
39 try:
40 ed = os.environ['EDITOR']
41 if not PY3:
42 ed = ed.decode()
43 return ed
44 except KeyError:
45 pass
46 except UnicodeError:
47 warn("$EDITOR environment variable is not pure ASCII. Using platform "
48 "default editor.")
49
50 if os.name == 'posix':
51 return 'vi' # the only one guaranteed to be there!
52 else:
53 return 'notepad' # same in Windows!
54
55
56 if sys.stdin and sys.stdout and sys.stderr:
57 _is_tty = (sys.stdin.isatty()) and (sys.stdout.isatty()) and (sys.stderr.isatty())
58 else:
59 _is_tty = False
60
61
62 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
63
64 class TerminalInteractiveShell(InteractiveShell):
65 colors_force = True
66
67 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
68 'to reserve for the completion menu'
69 ).tag(config=True)
70
71 def _space_for_menu_changed(self, old, new):
72 self._update_layout()
73
74 pt_cli = None
75 debugger_history = None
76
77 simple_prompt = Bool(_use_simple_prompt,
78 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
79
80 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
81 IPython own testing machinery, and emacs inferior-shell integration through elpy.
82
83 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
84 environment variable is set, or the current terminal is not a tty.
85
86 """
87 ).tag(config=True)
88
89 @property
90 def debugger_cls(self):
91 return Pdb if self.simple_prompt else TerminalPdb
92
93 autoedit_syntax = Bool(False,
94 help="auto editing of files with syntax errors.",
95 ).tag(config=True)
96
97
98 confirm_exit = Bool(True,
99 help="""
100 Set to confirm when you try to exit IPython with an EOF (Control-D
101 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
102 you can force a direct exit without any confirmation.""",
103 ).tag(config=True)
104
105 editing_mode = Unicode('emacs',
106 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
107 ).tag(config=True)
108
109 mouse_support = Bool(False,
110 help="Enable mouse support in the prompt"
111 ).tag(config=True)
112
113 highlighting_style = Unicode('default',
114 help="The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles())
115 ).tag(config=True)
116
117
118 @observe('highlighting_style')
119 def _highlighting_style_changed(self, change):
120 self._style = self._make_style_from_name(self.highlighting_style)
121
122 highlighting_style_overrides = Dict(
123 help="Override highlighting format for specific tokens"
124 ).tag(config=True)
125
126 editor = Unicode(get_default_editor(),
127 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
128 ).tag(config=True)
129
130 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
131
132 prompts = Instance(Prompts)
133
134 @default('prompts')
135 def _prompts_default(self):
136 return self.prompts_class(self)
137
138 @observe('prompts')
139 def _(self, change):
140 self._update_layout()
141
142 @default('displayhook_class')
143 def _displayhook_class_default(self):
144 return RichPromptDisplayHook
145
146 term_title = Bool(True,
147 help="Automatically set the terminal title"
148 ).tag(config=True)
149
150 display_completions_in_columns = Bool(False,
151 help="Display a multi column completion menu.",
152 ).tag(config=True)
153
154 highlight_matching_brackets = Bool(True,
155 help="Highlight matching brackets .",
156 ).tag(config=True)
157
158 @observe('term_title')
159 def init_term_title(self, change=None):
160 # Enable or disable the terminal title.
161 if self.term_title:
162 toggle_set_term_title(True)
163 set_term_title('IPython: ' + abbrev_cwd())
164 else:
165 toggle_set_term_title(False)
166
167 def init_display_formatter(self):
168 super(TerminalInteractiveShell, self).init_display_formatter()
169 # terminal only supports plain text
170 self.display_formatter.active_types = ['text/plain']
171
172 def init_prompt_toolkit_cli(self):
173 self._app = None
174 if self.simple_prompt:
175 # Fall back to plain non-interactive output for tests.
176 # This is very limited, and only accepts a single line.
177 def prompt():
178 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
179 self.prompt_for_code = prompt
180 return
181
182 kbmanager = KeyBindingManager.for_prompt()
183 insert_mode = ViInsertMode() | EmacsInsertMode()
184 # Ctrl+J == Enter, seemingly
185 @kbmanager.registry.add_binding(Keys.ControlJ,
186 filter=(HasFocus(DEFAULT_BUFFER)
187 & ~HasSelection()
188 & insert_mode
189 ))
190 def _(event):
191 b = event.current_buffer
192 d = b.document
193
194 if b.complete_state:
195 cc = b.complete_state.current_completion
196 if cc:
197 b.apply_completion(cc)
198 else:
199 b.cancel_completion()
200 return
201
202 if not (d.on_last_line or d.cursor_position_row >= d.line_count
203 - d.empty_line_count_at_the_end()):
204 b.newline()
205 return
206
207 status, indent = self.input_splitter.check_complete(d.text + '\n')
208
209 if (status != 'incomplete') and b.accept_action.is_returnable:
210 b.accept_action.validate_and_handle(event.cli, b)
211 else:
212 b.insert_text('\n' + (' ' * (indent or 0)))
213
214 @kbmanager.registry.add_binding(Keys.ControlP, filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER)))
215 def _previous_history_or_previous_completion(event):
216 """
217 Control-P in vi edit mode on readline is history next, unlike default prompt toolkit.
218
219 If completer is open this still select previous completion.
220 """
221 event.current_buffer.auto_up()
222
223 @kbmanager.registry.add_binding(Keys.ControlN, filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER)))
224 def _next_history_or_next_completion(event):
225 """
226 Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit.
227
228 If completer is open this still select next completion.
229 """
230 event.current_buffer.auto_down()
231
232 @kbmanager.registry.add_binding(Keys.ControlG, filter=(
233 HasFocus(DEFAULT_BUFFER) & HasCompletions()
234 ))
235 def _dismiss_completion(event):
236 b = event.current_buffer
237 if b.complete_state:
238 b.cancel_completion()
239
240 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER))
241 def _reset_buffer(event):
242 b = event.current_buffer
243 if b.complete_state:
244 b.cancel_completion()
245 else:
246 b.reset()
247
248 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER))
249 def _reset_search_buffer(event):
250 if event.current_buffer.document.text:
251 event.current_buffer.reset()
252 else:
253 event.cli.push_focus(DEFAULT_BUFFER)
254
255 supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP'))
256
257 @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend)
258 def _suspend_to_bg(event):
259 event.cli.suspend_to_background()
260
261 @Condition
262 def cursor_in_leading_ws(cli):
263 before = cli.application.buffer.document.current_line_before_cursor
264 return (not before) or before.isspace()
265
266 # Ctrl+I == Tab
267 @kbmanager.registry.add_binding(Keys.ControlI,
268 filter=(HasFocus(DEFAULT_BUFFER)
269 & ~HasSelection()
270 & insert_mode
271 & cursor_in_leading_ws
272 ))
273 def _indent_buffer(event):
274 event.current_buffer.insert_text(' ' * 4)
275
276 if sys.platform == 'win32':
277 from IPython.lib.clipboard import (ClipboardEmpty,
278 win32_clipboard_get, tkinter_clipboard_get)
279 @kbmanager.registry.add_binding(Keys.ControlV,
280 filter=(HasFocus(DEFAULT_BUFFER) & ~ViMode()))
281 def _paste(event):
282 try:
283 text = win32_clipboard_get()
284 except TryNext:
285 try:
286 text = tkinter_clipboard_get()
287 except (TryNext, ClipboardEmpty):
288 return
289 except ClipboardEmpty:
290 return
291 event.current_buffer.insert_text(text.replace('\t', ' ' * 4))
292
293 # Pre-populate history from IPython's history database
294 history = InMemoryHistory()
295 last_cell = u""
296 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
297 include_latest=True):
298 # Ignore blank lines and consecutive duplicates
299 cell = cell.rstrip()
300 if cell and (cell != last_cell):
301 history.append(cell)
302
303 self._style = self._make_style_from_name(self.highlighting_style)
304 style = DynamicStyle(lambda: self._style)
305
306 editing_mode = getattr(EditingMode, self.editing_mode.upper())
307
308 self._app = create_prompt_application(
309 editing_mode=editing_mode,
310 key_bindings_registry=kbmanager.registry,
311 history=history,
312 completer=IPythonPTCompleter(self.Completer),
313 enable_history_search=True,
314 style=style,
315 mouse_support=self.mouse_support,
316 **self._layout_options()
317 )
318 self._eventloop = create_eventloop(self.inputhook)
319 self.pt_cli = CommandLineInterface(self._app, eventloop=self._eventloop)
320
321 def _make_style_from_name(self, name):
322 """
323 Small wrapper that make an IPython compatible style from a style name
324
325 We need that to add style for prompt ... etc.
326 """
327 style_cls = get_style_by_name(name)
328 style_overrides = {
329 Token.Prompt: '#009900',
330 Token.PromptNum: '#00ff00 bold',
331 Token.OutPrompt: '#990000',
332 Token.OutPromptNum: '#ff0000 bold',
333 }
334 if name == 'default':
335 style_cls = get_style_by_name('default')
336 # The default theme needs to be visible on both a dark background
337 # and a light background, because we can't tell what the terminal
338 # looks like. These tweaks to the default theme help with that.
339 style_overrides.update({
340 Token.Number: '#007700',
341 Token.Operator: 'noinherit',
342 Token.String: '#BB6622',
343 Token.Name.Function: '#2080D0',
344 Token.Name.Class: 'bold #2080D0',
345 Token.Name.Namespace: 'bold #2080D0',
346 })
347 style_overrides.update(self.highlighting_style_overrides)
348 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
349 style_dict=style_overrides)
350
351 return style
352
353 def _layout_options(self):
354 """
355 Return the current layout option for the current Terminal InteractiveShell
356 """
357 return {
358 'lexer':IPythonPTLexer(),
359 'reserve_space_for_menu':self.space_for_menu,
360 'get_prompt_tokens':self.prompts.in_prompt_tokens,
361 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
362 'multiline':True,
363 'display_completions_in_columns': self.display_completions_in_columns,
364
365 # Highlight matching brackets, but only when this setting is
366 # enabled, and only when the DEFAULT_BUFFER has the focus.
367 'extra_input_processors': [ConditionalProcessor(
368 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
369 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
370 Condition(lambda cli: self.highlight_matching_brackets))],
371 }
372
373 def _update_layout(self):
374 """
375 Ask for a re computation of the application layout, if for example ,
376 some configuration options have changed.
377 """
378 if getattr(self, '._app', None):
379 self._app.layout = create_prompt_layout(**self._layout_options())
380
381 def prompt_for_code(self):
382 document = self.pt_cli.run(
383 pre_run=self.pre_prompt, reset_current_buffer=True)
384 return document.text
385
386 def init_io(self):
387 if sys.platform not in {'win32', 'cli'}:
388 return
389
390 import win_unicode_console
391 import colorama
392
393 win_unicode_console.enable()
394 colorama.init()
395
396 # For some reason we make these wrappers around stdout/stderr.
397 # For now, we need to reset them so all output gets coloured.
398 # https://github.com/ipython/ipython/issues/8669
399 from IPython.utils import io
400 io.stdout = io.IOStream(sys.stdout)
401 io.stderr = io.IOStream(sys.stderr)
402
403 def init_magics(self):
404 super(TerminalInteractiveShell, self).init_magics()
405 self.register_magics(TerminalMagics)
406
407 def init_alias(self):
408 # The parent class defines aliases that can be safely used with any
409 # frontend.
410 super(TerminalInteractiveShell, self).init_alias()
411
412 # Now define aliases that only make sense on the terminal, because they
413 # need direct access to the console in a way that we can't emulate in
414 # GUI or web frontend
415 if os.name == 'posix':
416 for cmd in ['clear', 'more', 'less', 'man']:
417 self.alias_manager.soft_define_alias(cmd, cmd)
418
419
420 def __init__(self, *args, **kwargs):
421 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
422 self.init_prompt_toolkit_cli()
423 self.init_term_title()
424 self.keep_running = True
425
426 self.debugger_history = InMemoryHistory()
427
428 def ask_exit(self):
429 self.keep_running = False
430
431 rl_next_input = None
432
433 def pre_prompt(self):
434 if self.rl_next_input:
435 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
436 self.rl_next_input = None
437
438 def interact(self):
439 while self.keep_running:
440 print(self.separate_in, end='')
441
442 try:
443 code = self.prompt_for_code()
444 except EOFError:
445 if (not self.confirm_exit) \
446 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
447 self.ask_exit()
448
449 else:
450 if code:
451 self.run_cell(code, store_history=True)
452 if self.autoedit_syntax and self.SyntaxTB.last_syntax_error:
453 self.edit_syntax_error()
454
455 def mainloop(self):
456 # An extra layer of protection in case someone mashing Ctrl-C breaks
457 # out of our internal code.
458 while True:
459 try:
460 self.interact()
461 break
462 except KeyboardInterrupt:
463 print("\nKeyboardInterrupt escaped interact()\n")
464
465 if hasattr(self, '_eventloop'):
466 self._eventloop.close()
467
468 _inputhook = None
469 def inputhook(self, context):
470 if self._inputhook is not None:
471 self._inputhook(context)
472
473 def enable_gui(self, gui=None):
474 if gui:
475 self._inputhook = get_inputhook_func(gui)
476 else:
477 self._inputhook = None
478
479 # Methods to support auto-editing of SyntaxErrors:
480
481 def edit_syntax_error(self):
482 """The bottom half of the syntax error handler called in the main loop.
483
484 Loop until syntax error is fixed or user cancels.
485 """
486
487 while self.SyntaxTB.last_syntax_error:
488 # copy and clear last_syntax_error
489 err = self.SyntaxTB.clear_err_state()
490 if not self._should_recompile(err):
491 return
492 try:
493 # may set last_syntax_error again if a SyntaxError is raised
494 self.safe_execfile(err.filename, self.user_ns)
495 except:
496 self.showtraceback()
497 else:
498 try:
499 with open(err.filename) as f:
500 # This should be inside a display_trap block and I
501 # think it is.
502 sys.displayhook(f.read())
503 except:
504 self.showtraceback()
505
506 def _should_recompile(self, e):
507 """Utility routine for edit_syntax_error"""
508
509 if e.filename in ('<ipython console>', '<input>', '<string>',
510 '<console>', '<BackgroundJob compilation>',
511 None):
512 return False
513 try:
514 if (self.autoedit_syntax and
515 not self.ask_yes_no(
516 'Return to editor to correct syntax error? '
517 '[Y/n] ', 'y')):
518 return False
519 except EOFError:
520 return False
521
522 def int0(x):
523 try:
524 return int(x)
525 except TypeError:
526 return 0
527
528 # always pass integer line and offset values to editor hook
529 try:
530 self.hooks.fix_error_editor(e.filename,
531 int0(e.lineno), int0(e.offset),
532 e.msg)
533 except TryNext:
534 warn('Could not open editor')
535 return False
536 return True
537
538 # Run !system commands directly, not through pipes, so terminal programs
539 # work correctly.
540 system = InteractiveShell.system_raw
541
542 def auto_rewrite_input(self, cmd):
543 """Overridden from the parent class to use fancy rewriting prompt"""
544 if not self.show_rewritten_input:
545 return
546
547 tokens = self.prompts.rewrite_prompt_tokens()
548 if self.pt_cli:
549 self.pt_cli.print_tokens(tokens)
550 print(cmd)
551 else:
552 prompt = ''.join(s for t, s in tokens)
553 print(prompt, cmd, sep='')
554
555 _prompts_before = None
556 def switch_doctest_mode(self, mode):
557 """Switch prompts to classic for %doctest_mode"""
558 if mode:
559 self._prompts_before = self.prompts
560 self.prompts = ClassicPrompts(self)
561 elif self._prompts_before:
562 self.prompts = self._prompts_before
563 self._prompts_before = None
564
565
566 InteractiveShellABC.register(TerminalInteractiveShell)
567
568 if __name__ == '__main__':
569 TerminalInteractiveShell.instance().interact()
7 This file will be removed in 5.0 rc or final.
8 """)
@@ -34,12 +34,35 b' that same event.'
34 34 Integration with pydb has been removed since pydb development has been stopped
35 35 since 2012, and pydb is not installable from PyPI
36 36
37 IPython 5.0 now uses prompt_toolkit, so any setting that affects ``readline`` will
38 have no effect, and has likely been replaced by a configuration option on
39 IPython itself.
40 37
41 the `PromptManager` class have been removed, and the prompt machinery simplified.
42 See `TerminalINteractiveShell.prompts` configurable for how to setup your prompts.
38
39 Replacement of readline in TerminalInteractiveShell and PDB
40 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
41
42 IPython 5.0 now uses ``prompt_toolkit``. The
43 ``IPython.terminal.interactiveshell.TerminalInteractiveShell`` now uses
44 ``prompt_toolkit``. It is an almost complete rewrite, so many settings have
45 thus changed or disappeared. The class keep the same name to avoid breaking
46 user configuration for the options which names is unchanged.
47
48 The usage of ``prompt_toolkit`` is accompanied by a complete removal of all
49 code, using ``readline``. A particular effect of not using `readline` anymore
50 is that `.inputrc` settings are note effective anymore. Options having similar
51 effects have likely been replaced by a configuration option on IPython itself
52 (e.g: vi input mode).
53
54 The `PromptManager` class have been removed, and the prompt machinery simplified.
55 See `TerminalInteractiveShell.prompts` configurable for how to setup your prompts.
56
57 .. note::
58
59 During developement and beta cycle, ``TerminalInteractiveShell`` was
60 temporarly moved to ``IPtyhon.terminal.ptshell``.
61
62
63 Most of the above remarks also affect `IPython.core.debugger.Pdb`, the `%debug`
64 and `%pdb` magic which do not use readline anymore either.
65
43 66
44 67
45 68
General Comments 0
You need to be logged in to leave comments. Login now