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