##// END OF EJS Templates
Remove unused import
Thomas Kluyver -
Show More
@@ -1,540 +1,540 b''
1 1 """IPython terminal interface using prompt_toolkit"""
2 2
3 3 import os
4 4 import sys
5 5 import warnings
6 6 from warnings import warn
7 7
8 8 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
9 9 from IPython.utils import io
10 10 from IPython.utils.py3compat import input, cast_unicode_py2
11 11 from IPython.utils.terminal import toggle_set_term_title, set_term_title
12 12 from IPython.utils.process import abbrev_cwd
13 13 from traitlets import (
14 14 Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum, Union,
15 15 Any,
16 16 )
17 17
18 18 from prompt_toolkit.document import Document
19 19 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
20 20 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
21 21 from prompt_toolkit.history import InMemoryHistory
22 22 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout, create_output
23 23 from prompt_toolkit.interface import CommandLineInterface
24 24 from prompt_toolkit.key_binding.manager import KeyBindingManager
25 25 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
26 26 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
27 27
28 from pygments.styles import get_style_by_name, get_all_styles
28 from pygments.styles import get_style_by_name
29 29 from pygments.style import Style
30 30 from pygments.token import Token
31 31
32 32 from .debugger import TerminalPdb, Pdb
33 33 from .magics import TerminalMagics
34 34 from .pt_inputhooks import get_inputhook_name_and_func
35 35 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
36 36 from .ptutils import IPythonPTCompleter, IPythonPTLexer
37 37 from .shortcuts import register_ipython_shortcuts
38 38
39 39 DISPLAY_BANNER_DEPRECATED = object()
40 40
41 41
42 42 class _NoStyle(Style): pass
43 43
44 44
45 45
46 46 _style_overrides_light_bg = {
47 47 Token.Prompt: '#0000ff',
48 48 Token.PromptNum: '#0000ee bold',
49 49 Token.OutPrompt: '#cc0000',
50 50 Token.OutPromptNum: '#bb0000 bold',
51 51 }
52 52
53 53 _style_overrides_linux = {
54 54 Token.Prompt: '#00cc00',
55 55 Token.PromptNum: '#00bb00 bold',
56 56 Token.OutPrompt: '#cc0000',
57 57 Token.OutPromptNum: '#bb0000 bold',
58 58 }
59 59
60 60
61 61
62 62 def get_default_editor():
63 63 try:
64 64 return os.environ['EDITOR']
65 65 except KeyError:
66 66 pass
67 67 except UnicodeError:
68 68 warn("$EDITOR environment variable is not pure ASCII. Using platform "
69 69 "default editor.")
70 70
71 71 if os.name == 'posix':
72 72 return 'vi' # the only one guaranteed to be there!
73 73 else:
74 74 return 'notepad' # same in Windows!
75 75
76 76 # conservatively check for tty
77 77 # overridden streams can result in things like:
78 78 # - sys.stdin = None
79 79 # - no isatty method
80 80 for _name in ('stdin', 'stdout', 'stderr'):
81 81 _stream = getattr(sys, _name)
82 82 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
83 83 _is_tty = False
84 84 break
85 85 else:
86 86 _is_tty = True
87 87
88 88
89 89 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
90 90
91 91 class TerminalInteractiveShell(InteractiveShell):
92 92 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
93 93 'to reserve for the completion menu'
94 94 ).tag(config=True)
95 95
96 96 def _space_for_menu_changed(self, old, new):
97 97 self._update_layout()
98 98
99 99 pt_cli = None
100 100 debugger_history = None
101 101 _pt_app = None
102 102
103 103 simple_prompt = Bool(_use_simple_prompt,
104 104 help="""Use `raw_input` for the REPL, without completion and prompt colors.
105 105
106 106 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
107 107 IPython own testing machinery, and emacs inferior-shell integration through elpy.
108 108
109 109 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
110 110 environment variable is set, or the current terminal is not a tty."""
111 111 ).tag(config=True)
112 112
113 113 @property
114 114 def debugger_cls(self):
115 115 return Pdb if self.simple_prompt else TerminalPdb
116 116
117 117 confirm_exit = Bool(True,
118 118 help="""
119 119 Set to confirm when you try to exit IPython with an EOF (Control-D
120 120 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
121 121 you can force a direct exit without any confirmation.""",
122 122 ).tag(config=True)
123 123
124 124 editing_mode = Unicode('emacs',
125 125 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
126 126 ).tag(config=True)
127 127
128 128 mouse_support = Bool(False,
129 129 help="Enable mouse support in the prompt\n(Note: prevents selecting text with the mouse)"
130 130 ).tag(config=True)
131 131
132 132 # We don't load the list of styles for the help string, because loading
133 133 # Pygments plugins takes time and can cause unexpected errors.
134 134 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
135 135 help="""The name or class of a Pygments style to use for syntax
136 136 highlighting. To see available styles, run `pygmentize -L styles`."""
137 137 ).tag(config=True)
138 138
139 139
140 140 @observe('highlighting_style')
141 141 @observe('colors')
142 142 def _highlighting_style_changed(self, change):
143 143 self.refresh_style()
144 144
145 145 def refresh_style(self):
146 146 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
147 147
148 148
149 149 highlighting_style_overrides = Dict(
150 150 help="Override highlighting format for specific tokens"
151 151 ).tag(config=True)
152 152
153 153 true_color = Bool(False,
154 154 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
155 155 "If your terminal supports true color, the following command "
156 156 "should print 'TRUECOLOR' in orange: "
157 157 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
158 158 ).tag(config=True)
159 159
160 160 editor = Unicode(get_default_editor(),
161 161 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
162 162 ).tag(config=True)
163 163
164 164 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
165 165
166 166 prompts = Instance(Prompts)
167 167
168 168 @default('prompts')
169 169 def _prompts_default(self):
170 170 return self.prompts_class(self)
171 171
172 172 @observe('prompts')
173 173 def _(self, change):
174 174 self._update_layout()
175 175
176 176 @default('displayhook_class')
177 177 def _displayhook_class_default(self):
178 178 return RichPromptDisplayHook
179 179
180 180 term_title = Bool(True,
181 181 help="Automatically set the terminal title"
182 182 ).tag(config=True)
183 183
184 184 term_title_format = Unicode("IPython: {cwd}",
185 185 help="Customize the terminal title format. This is a python format string. " +
186 186 "Available substitutions are: {cwd}."
187 187 ).tag(config=True)
188 188
189 189 display_completions = Enum(('column', 'multicolumn','readlinelike'),
190 190 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
191 191 "'readlinelike'. These options are for `prompt_toolkit`, see "
192 192 "`prompt_toolkit` documentation for more information."
193 193 ),
194 194 default_value='multicolumn').tag(config=True)
195 195
196 196 highlight_matching_brackets = Bool(True,
197 197 help="Highlight matching brackets.",
198 198 ).tag(config=True)
199 199
200 200 extra_open_editor_shortcuts = Bool(False,
201 201 help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
202 202 "This is in addition to the F2 binding, which is always enabled."
203 203 ).tag(config=True)
204 204
205 205 handle_return = Any(None,
206 206 help="Provide an alternative handler to be called when the user presses "
207 207 "Return. This is an advanced option intended for debugging, which "
208 208 "may be changed or removed in later releases."
209 209 ).tag(config=True)
210 210
211 211 @observe('term_title')
212 212 def init_term_title(self, change=None):
213 213 # Enable or disable the terminal title.
214 214 if self.term_title:
215 215 toggle_set_term_title(True)
216 216 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
217 217 else:
218 218 toggle_set_term_title(False)
219 219
220 220 def init_display_formatter(self):
221 221 super(TerminalInteractiveShell, self).init_display_formatter()
222 222 # terminal only supports plain text
223 223 self.display_formatter.active_types = ['text/plain']
224 224 # disable `_ipython_display_`
225 225 self.display_formatter.ipython_display_formatter.enabled = False
226 226
227 227 def init_prompt_toolkit_cli(self):
228 228 if self.simple_prompt:
229 229 # Fall back to plain non-interactive output for tests.
230 230 # This is very limited, and only accepts a single line.
231 231 def prompt():
232 232 isp = self.input_splitter
233 233 prompt_text = "".join(x[1] for x in self.prompts.in_prompt_tokens())
234 234 prompt_continuation = "".join(x[1] for x in self.prompts.continuation_prompt_tokens())
235 235 while isp.push_accepts_more():
236 236 line = cast_unicode_py2(input(prompt_text))
237 237 isp.push(line)
238 238 prompt_text = prompt_continuation
239 239 return isp.source_reset()
240 240 self.prompt_for_code = prompt
241 241 return
242 242
243 243 # Set up keyboard shortcuts
244 244 kbmanager = KeyBindingManager.for_prompt(
245 245 enable_open_in_editor=self.extra_open_editor_shortcuts,
246 246 )
247 247 register_ipython_shortcuts(kbmanager.registry, self)
248 248
249 249 # Pre-populate history from IPython's history database
250 250 history = InMemoryHistory()
251 251 last_cell = u""
252 252 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
253 253 include_latest=True):
254 254 # Ignore blank lines and consecutive duplicates
255 255 cell = cell.rstrip()
256 256 if cell and (cell != last_cell):
257 257 history.append(cell)
258 258 last_cell = cell
259 259
260 260 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
261 261 self.style = DynamicStyle(lambda: self._style)
262 262
263 263 editing_mode = getattr(EditingMode, self.editing_mode.upper())
264 264
265 265 def patch_stdout(**kwargs):
266 266 return self.pt_cli.patch_stdout_context(**kwargs)
267 267
268 268 self._pt_app = create_prompt_application(
269 269 editing_mode=editing_mode,
270 270 key_bindings_registry=kbmanager.registry,
271 271 history=history,
272 272 completer=IPythonPTCompleter(shell=self,
273 273 patch_stdout=patch_stdout),
274 274 enable_history_search=True,
275 275 style=self.style,
276 276 mouse_support=self.mouse_support,
277 277 **self._layout_options()
278 278 )
279 279 self._eventloop = create_eventloop(self.inputhook)
280 280 self.pt_cli = CommandLineInterface(
281 281 self._pt_app, eventloop=self._eventloop,
282 282 output=create_output(true_color=self.true_color))
283 283
284 284 def _make_style_from_name_or_cls(self, name_or_cls):
285 285 """
286 286 Small wrapper that make an IPython compatible style from a style name
287 287
288 288 We need that to add style for prompt ... etc.
289 289 """
290 290 style_overrides = {}
291 291 if name_or_cls == 'legacy':
292 292 legacy = self.colors.lower()
293 293 if legacy == 'linux':
294 294 style_cls = get_style_by_name('monokai')
295 295 style_overrides = _style_overrides_linux
296 296 elif legacy == 'lightbg':
297 297 style_overrides = _style_overrides_light_bg
298 298 style_cls = get_style_by_name('pastie')
299 299 elif legacy == 'neutral':
300 300 # The default theme needs to be visible on both a dark background
301 301 # and a light background, because we can't tell what the terminal
302 302 # looks like. These tweaks to the default theme help with that.
303 303 style_cls = get_style_by_name('default')
304 304 style_overrides.update({
305 305 Token.Number: '#007700',
306 306 Token.Operator: 'noinherit',
307 307 Token.String: '#BB6622',
308 308 Token.Name.Function: '#2080D0',
309 309 Token.Name.Class: 'bold #2080D0',
310 310 Token.Name.Namespace: 'bold #2080D0',
311 311 Token.Prompt: '#009900',
312 312 Token.PromptNum: '#00ff00 bold',
313 313 Token.OutPrompt: '#990000',
314 314 Token.OutPromptNum: '#ff0000 bold',
315 315 })
316 316
317 317 # Hack: Due to limited color support on the Windows console
318 318 # the prompt colors will be wrong without this
319 319 if os.name == 'nt':
320 320 style_overrides.update({
321 321 Token.Prompt: '#ansidarkgreen',
322 322 Token.PromptNum: '#ansigreen bold',
323 323 Token.OutPrompt: '#ansidarkred',
324 324 Token.OutPromptNum: '#ansired bold',
325 325 })
326 326 elif legacy =='nocolor':
327 327 style_cls=_NoStyle
328 328 style_overrides = {}
329 329 else :
330 330 raise ValueError('Got unknown colors: ', legacy)
331 331 else :
332 332 if isinstance(name_or_cls, str):
333 333 style_cls = get_style_by_name(name_or_cls)
334 334 else:
335 335 style_cls = name_or_cls
336 336 style_overrides = {
337 337 Token.Prompt: '#009900',
338 338 Token.PromptNum: '#00ff00 bold',
339 339 Token.OutPrompt: '#990000',
340 340 Token.OutPromptNum: '#ff0000 bold',
341 341 }
342 342 style_overrides.update(self.highlighting_style_overrides)
343 343 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
344 344 style_dict=style_overrides)
345 345
346 346 return style
347 347
348 348 def _layout_options(self):
349 349 """
350 350 Return the current layout option for the current Terminal InteractiveShell
351 351 """
352 352 return {
353 353 'lexer':IPythonPTLexer(),
354 354 'reserve_space_for_menu':self.space_for_menu,
355 355 'get_prompt_tokens':self.prompts.in_prompt_tokens,
356 356 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
357 357 'multiline':True,
358 358 'display_completions_in_columns': (self.display_completions == 'multicolumn'),
359 359
360 360 # Highlight matching brackets, but only when this setting is
361 361 # enabled, and only when the DEFAULT_BUFFER has the focus.
362 362 'extra_input_processors': [ConditionalProcessor(
363 363 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
364 364 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
365 365 Condition(lambda cli: self.highlight_matching_brackets))],
366 366 }
367 367
368 368 def _update_layout(self):
369 369 """
370 370 Ask for a re computation of the application layout, if for example ,
371 371 some configuration options have changed.
372 372 """
373 373 if self._pt_app:
374 374 self._pt_app.layout = create_prompt_layout(**self._layout_options())
375 375
376 376 def prompt_for_code(self):
377 377 with self.pt_cli.patch_stdout_context(raw=True):
378 378 document = self.pt_cli.run(
379 379 pre_run=self.pre_prompt, reset_current_buffer=True)
380 380 return document.text
381 381
382 382 def enable_win_unicode_console(self):
383 383 if sys.version_info >= (3, 6):
384 384 # Since PEP 528, Python uses the unicode APIs for the Windows
385 385 # console by default, so WUC shouldn't be needed.
386 386 return
387 387
388 388 import win_unicode_console
389 389 win_unicode_console.enable()
390 390
391 391 def init_io(self):
392 392 if sys.platform not in {'win32', 'cli'}:
393 393 return
394 394
395 395 self.enable_win_unicode_console()
396 396
397 397 import colorama
398 398 colorama.init()
399 399
400 400 # For some reason we make these wrappers around stdout/stderr.
401 401 # For now, we need to reset them so all output gets coloured.
402 402 # https://github.com/ipython/ipython/issues/8669
403 403 # io.std* are deprecated, but don't show our own deprecation warnings
404 404 # during initialization of the deprecated API.
405 405 with warnings.catch_warnings():
406 406 warnings.simplefilter('ignore', DeprecationWarning)
407 407 io.stdout = io.IOStream(sys.stdout)
408 408 io.stderr = io.IOStream(sys.stderr)
409 409
410 410 def init_magics(self):
411 411 super(TerminalInteractiveShell, self).init_magics()
412 412 self.register_magics(TerminalMagics)
413 413
414 414 def init_alias(self):
415 415 # The parent class defines aliases that can be safely used with any
416 416 # frontend.
417 417 super(TerminalInteractiveShell, self).init_alias()
418 418
419 419 # Now define aliases that only make sense on the terminal, because they
420 420 # need direct access to the console in a way that we can't emulate in
421 421 # GUI or web frontend
422 422 if os.name == 'posix':
423 423 for cmd in ['clear', 'more', 'less', 'man']:
424 424 self.alias_manager.soft_define_alias(cmd, cmd)
425 425
426 426
427 427 def __init__(self, *args, **kwargs):
428 428 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
429 429 self.init_prompt_toolkit_cli()
430 430 self.init_term_title()
431 431 self.keep_running = True
432 432
433 433 self.debugger_history = InMemoryHistory()
434 434
435 435 def ask_exit(self):
436 436 self.keep_running = False
437 437
438 438 rl_next_input = None
439 439
440 440 def pre_prompt(self):
441 441 if self.rl_next_input:
442 442 # We can't set the buffer here, because it will be reset just after
443 443 # this. Adding a callable to pre_run_callables does what we need
444 444 # after the buffer is reset.
445 445 s = self.rl_next_input
446 446 def set_doc():
447 447 self.pt_cli.application.buffer.document = Document(s)
448 448 if hasattr(self.pt_cli, 'pre_run_callables'):
449 449 self.pt_cli.pre_run_callables.append(set_doc)
450 450 else:
451 451 # Older version of prompt_toolkit; it's OK to set the document
452 452 # directly here.
453 453 set_doc()
454 454 self.rl_next_input = None
455 455
456 456 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
457 457
458 458 if display_banner is not DISPLAY_BANNER_DEPRECATED:
459 459 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
460 460
461 461 self.keep_running = True
462 462 while self.keep_running:
463 463 print(self.separate_in, end='')
464 464
465 465 try:
466 466 code = self.prompt_for_code()
467 467 except EOFError:
468 468 if (not self.confirm_exit) \
469 469 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
470 470 self.ask_exit()
471 471
472 472 else:
473 473 if code:
474 474 self.run_cell(code, store_history=True)
475 475
476 476 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
477 477 # An extra layer of protection in case someone mashing Ctrl-C breaks
478 478 # out of our internal code.
479 479 if display_banner is not DISPLAY_BANNER_DEPRECATED:
480 480 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
481 481 while True:
482 482 try:
483 483 self.interact()
484 484 break
485 485 except KeyboardInterrupt as e:
486 486 print("\n%s escaped interact()\n" % type(e).__name__)
487 487 finally:
488 488 # An interrupt during the eventloop will mess up the
489 489 # internal state of the prompt_toolkit library.
490 490 # Stopping the eventloop fixes this, see
491 491 # https://github.com/ipython/ipython/pull/9867
492 492 if hasattr(self, '_eventloop'):
493 493 self._eventloop.stop()
494 494
495 495 _inputhook = None
496 496 def inputhook(self, context):
497 497 if self._inputhook is not None:
498 498 self._inputhook(context)
499 499
500 500 active_eventloop = None
501 501 def enable_gui(self, gui=None):
502 502 if gui:
503 503 self.active_eventloop, self._inputhook =\
504 504 get_inputhook_name_and_func(gui)
505 505 else:
506 506 self.active_eventloop = self._inputhook = None
507 507
508 508 # Run !system commands directly, not through pipes, so terminal programs
509 509 # work correctly.
510 510 system = InteractiveShell.system_raw
511 511
512 512 def auto_rewrite_input(self, cmd):
513 513 """Overridden from the parent class to use fancy rewriting prompt"""
514 514 if not self.show_rewritten_input:
515 515 return
516 516
517 517 tokens = self.prompts.rewrite_prompt_tokens()
518 518 if self.pt_cli:
519 519 self.pt_cli.print_tokens(tokens)
520 520 print(cmd)
521 521 else:
522 522 prompt = ''.join(s for t, s in tokens)
523 523 print(prompt, cmd, sep='')
524 524
525 525 _prompts_before = None
526 526 def switch_doctest_mode(self, mode):
527 527 """Switch prompts to classic for %doctest_mode"""
528 528 if mode:
529 529 self._prompts_before = self.prompts
530 530 self.prompts = ClassicPrompts(self)
531 531 elif self._prompts_before:
532 532 self.prompts = self._prompts_before
533 533 self._prompts_before = None
534 534 self._update_layout()
535 535
536 536
537 537 InteractiveShellABC.register(TerminalInteractiveShell)
538 538
539 539 if __name__ == '__main__':
540 540 TerminalInteractiveShell.instance().interact()
General Comments 0
You need to be logged in to leave comments. Login now