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