##// END OF EJS Templates
Merge pull request #13528 from algmyr/master...
Matthias Bussonnier -
r27537:935dedb4 merge
parent child Browse files
Show More
@@ -1,724 +1,742 b''
1 1 """IPython terminal interface using prompt_toolkit"""
2 2
3 3 import asyncio
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.async_helpers import get_asyncio_loop
10 10 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
11 11 from IPython.utils import io
12 12 from IPython.utils.py3compat import input
13 13 from IPython.utils.terminal import toggle_set_term_title, set_term_title, restore_term_title
14 14 from IPython.utils.process import abbrev_cwd
15 15 from traitlets import (
16 16 Bool,
17 17 Unicode,
18 18 Dict,
19 19 Integer,
20 20 observe,
21 21 Instance,
22 22 Type,
23 23 default,
24 24 Enum,
25 25 Union,
26 26 Any,
27 27 validate,
28 28 Float,
29 29 )
30 30
31 31 from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
32 32 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
33 33 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
34 34 from prompt_toolkit.formatted_text import PygmentsTokens
35 35 from prompt_toolkit.history import InMemoryHistory
36 36 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
37 37 from prompt_toolkit.output import ColorDepth
38 38 from prompt_toolkit.patch_stdout import patch_stdout
39 39 from prompt_toolkit.shortcuts import PromptSession, CompleteStyle, print_formatted_text
40 40 from prompt_toolkit.styles import DynamicStyle, merge_styles
41 41 from prompt_toolkit.styles.pygments import style_from_pygments_cls, style_from_pygments_dict
42 42 from prompt_toolkit import __version__ as ptk_version
43 43
44 44 from pygments.styles import get_style_by_name
45 45 from pygments.style import Style
46 46 from pygments.token import Token
47 47
48 48 from .debugger import TerminalPdb, Pdb
49 49 from .magics import TerminalMagics
50 50 from .pt_inputhooks import get_inputhook_name_and_func
51 51 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
52 52 from .ptutils import IPythonPTCompleter, IPythonPTLexer
53 53 from .shortcuts import create_ipython_shortcuts
54 54
55 55 PTK3 = ptk_version.startswith('3.')
56 56
57 57
58 58 class _NoStyle(Style): pass
59 59
60 60
61 61
62 62 _style_overrides_light_bg = {
63 63 Token.Prompt: '#ansibrightblue',
64 64 Token.PromptNum: '#ansiblue bold',
65 65 Token.OutPrompt: '#ansibrightred',
66 66 Token.OutPromptNum: '#ansired bold',
67 67 }
68 68
69 69 _style_overrides_linux = {
70 70 Token.Prompt: '#ansibrightgreen',
71 71 Token.PromptNum: '#ansigreen bold',
72 72 Token.OutPrompt: '#ansibrightred',
73 73 Token.OutPromptNum: '#ansired bold',
74 74 }
75 75
76 76 def get_default_editor():
77 77 try:
78 78 return os.environ['EDITOR']
79 79 except KeyError:
80 80 pass
81 81 except UnicodeError:
82 82 warn("$EDITOR environment variable is not pure ASCII. Using platform "
83 83 "default editor.")
84 84
85 85 if os.name == 'posix':
86 86 return 'vi' # the only one guaranteed to be there!
87 87 else:
88 88 return 'notepad' # same in Windows!
89 89
90 90 # conservatively check for tty
91 91 # overridden streams can result in things like:
92 92 # - sys.stdin = None
93 93 # - no isatty method
94 94 for _name in ('stdin', 'stdout', 'stderr'):
95 95 _stream = getattr(sys, _name)
96 96 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
97 97 _is_tty = False
98 98 break
99 99 else:
100 100 _is_tty = True
101 101
102 102
103 103 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
104 104
105 105 def black_reformat_handler(text_before_cursor):
106 106 """
107 107 We do not need to protect against error,
108 108 this is taken care at a higher level where any reformat error is ignored.
109 109 Indeed we may call reformatting on incomplete code.
110 110 """
111 111 import black
112 112
113 113 formatted_text = black.format_str(text_before_cursor, mode=black.FileMode())
114 114 if not text_before_cursor.endswith("\n") and formatted_text.endswith("\n"):
115 115 formatted_text = formatted_text[:-1]
116 116 return formatted_text
117 117
118 118
119 def yapf_reformat_handler(text_before_cursor):
120 from yapf.yapflib import file_resources
121 from yapf.yapflib import yapf_api
122
123 style_config = file_resources.GetDefaultStyleForDir(os.getcwd())
124 formatted_text, was_formatted = yapf_api.FormatCode(
125 text_before_cursor, style_config=style_config
126 )
127 if was_formatted:
128 if not text_before_cursor.endswith("\n") and formatted_text.endswith("\n"):
129 formatted_text = formatted_text[:-1]
130 return formatted_text
131 else:
132 return text_before_cursor
133
134
119 135 class TerminalInteractiveShell(InteractiveShell):
120 136 mime_renderers = Dict().tag(config=True)
121 137
122 138 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
123 139 'to reserve for the tab completion menu, '
124 140 'search history, ...etc, the height of '
125 141 'these menus will at most this value. '
126 142 'Increase it is you prefer long and skinny '
127 143 'menus, decrease for short and wide.'
128 144 ).tag(config=True)
129 145
130 146 pt_app = None
131 147 debugger_history = None
132 148
133 149 debugger_history_file = Unicode(
134 150 "~/.pdbhistory", help="File in which to store and read history"
135 151 ).tag(config=True)
136 152
137 153 simple_prompt = Bool(_use_simple_prompt,
138 154 help="""Use `raw_input` for the REPL, without completion and prompt colors.
139 155
140 156 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
141 157 IPython own testing machinery, and emacs inferior-shell integration through elpy.
142 158
143 159 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
144 160 environment variable is set, or the current terminal is not a tty."""
145 161 ).tag(config=True)
146 162
147 163 @property
148 164 def debugger_cls(self):
149 165 return Pdb if self.simple_prompt else TerminalPdb
150 166
151 167 confirm_exit = Bool(True,
152 168 help="""
153 169 Set to confirm when you try to exit IPython with an EOF (Control-D
154 170 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
155 171 you can force a direct exit without any confirmation.""",
156 172 ).tag(config=True)
157 173
158 174 editing_mode = Unicode('emacs',
159 175 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
160 176 ).tag(config=True)
161 177
162 178 emacs_bindings_in_vi_insert_mode = Bool(
163 179 True,
164 180 help="Add shortcuts from 'emacs' insert mode to 'vi' insert mode.",
165 181 ).tag(config=True)
166 182
167 183 modal_cursor = Bool(
168 184 True,
169 185 help="""
170 186 Cursor shape changes depending on vi mode: beam in vi insert mode,
171 187 block in nav mode, underscore in replace mode.""",
172 188 ).tag(config=True)
173 189
174 190 ttimeoutlen = Float(
175 191 0.01,
176 192 help="""The time in milliseconds that is waited for a key code
177 193 to complete.""",
178 194 ).tag(config=True)
179 195
180 196 timeoutlen = Float(
181 197 0.5,
182 198 help="""The time in milliseconds that is waited for a mapped key
183 199 sequence to complete.""",
184 200 ).tag(config=True)
185 201
186 202 autoformatter = Unicode(
187 203 "black",
188 help="Autoformatter to reformat Terminal code. Can be `'black'` or `None`",
204 help="Autoformatter to reformat Terminal code. Can be `'black'`, `'yapf'` or `None`",
189 205 allow_none=True
190 206 ).tag(config=True)
191 207
192 208 auto_match = Bool(
193 209 False,
194 210 help="""
195 211 Automatically add/delete closing bracket or quote when opening bracket or quote is entered/deleted.
196 212 Brackets: (), [], {}
197 213 Quotes: '', \"\"
198 214 """,
199 215 ).tag(config=True)
200 216
201 217 mouse_support = Bool(False,
202 218 help="Enable mouse support in the prompt\n(Note: prevents selecting text with the mouse)"
203 219 ).tag(config=True)
204 220
205 221 # We don't load the list of styles for the help string, because loading
206 222 # Pygments plugins takes time and can cause unexpected errors.
207 223 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
208 224 help="""The name or class of a Pygments style to use for syntax
209 225 highlighting. To see available styles, run `pygmentize -L styles`."""
210 226 ).tag(config=True)
211 227
212 228 @validate('editing_mode')
213 229 def _validate_editing_mode(self, proposal):
214 230 if proposal['value'].lower() == 'vim':
215 231 proposal['value']= 'vi'
216 232 elif proposal['value'].lower() == 'default':
217 233 proposal['value']= 'emacs'
218 234
219 235 if hasattr(EditingMode, proposal['value'].upper()):
220 236 return proposal['value'].lower()
221 237
222 238 return self.editing_mode
223 239
224 240
225 241 @observe('editing_mode')
226 242 def _editing_mode(self, change):
227 243 if self.pt_app:
228 244 self.pt_app.editing_mode = getattr(EditingMode, change.new.upper())
229 245
230 246 def _set_formatter(self, formatter):
231 247 if formatter is None:
232 248 self.reformat_handler = lambda x:x
233 249 elif formatter == 'black':
234 250 self.reformat_handler = black_reformat_handler
251 elif formatter == "yapf":
252 self.reformat_handler = yapf_reformat_handler
235 253 else:
236 254 raise ValueError
237 255
238 256 @observe("autoformatter")
239 257 def _autoformatter_changed(self, change):
240 258 formatter = change.new
241 259 self._set_formatter(formatter)
242 260
243 261 @observe('highlighting_style')
244 262 @observe('colors')
245 263 def _highlighting_style_changed(self, change):
246 264 self.refresh_style()
247 265
248 266 def refresh_style(self):
249 267 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
250 268
251 269
252 270 highlighting_style_overrides = Dict(
253 271 help="Override highlighting format for specific tokens"
254 272 ).tag(config=True)
255 273
256 274 true_color = Bool(False,
257 275 help="""Use 24bit colors instead of 256 colors in prompt highlighting.
258 276 If your terminal supports true color, the following command should
259 277 print ``TRUECOLOR`` in orange::
260 278
261 279 printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"
262 280 """,
263 281 ).tag(config=True)
264 282
265 283 editor = Unicode(get_default_editor(),
266 284 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
267 285 ).tag(config=True)
268 286
269 287 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
270 288
271 289 prompts = Instance(Prompts)
272 290
273 291 @default('prompts')
274 292 def _prompts_default(self):
275 293 return self.prompts_class(self)
276 294
277 295 # @observe('prompts')
278 296 # def _(self, change):
279 297 # self._update_layout()
280 298
281 299 @default('displayhook_class')
282 300 def _displayhook_class_default(self):
283 301 return RichPromptDisplayHook
284 302
285 303 term_title = Bool(True,
286 304 help="Automatically set the terminal title"
287 305 ).tag(config=True)
288 306
289 307 term_title_format = Unicode("IPython: {cwd}",
290 308 help="Customize the terminal title format. This is a python format string. " +
291 309 "Available substitutions are: {cwd}."
292 310 ).tag(config=True)
293 311
294 312 display_completions = Enum(('column', 'multicolumn','readlinelike'),
295 313 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
296 314 "'readlinelike'. These options are for `prompt_toolkit`, see "
297 315 "`prompt_toolkit` documentation for more information."
298 316 ),
299 317 default_value='multicolumn').tag(config=True)
300 318
301 319 highlight_matching_brackets = Bool(True,
302 320 help="Highlight matching brackets.",
303 321 ).tag(config=True)
304 322
305 323 extra_open_editor_shortcuts = Bool(False,
306 324 help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
307 325 "This is in addition to the F2 binding, which is always enabled."
308 326 ).tag(config=True)
309 327
310 328 handle_return = Any(None,
311 329 help="Provide an alternative handler to be called when the user presses "
312 330 "Return. This is an advanced option intended for debugging, which "
313 331 "may be changed or removed in later releases."
314 332 ).tag(config=True)
315 333
316 334 enable_history_search = Bool(True,
317 335 help="Allows to enable/disable the prompt toolkit history search"
318 336 ).tag(config=True)
319 337
320 338 autosuggestions_provider = Unicode(
321 339 "AutoSuggestFromHistory",
322 340 help="Specifies from which source automatic suggestions are provided. "
323 341 "Can be set to `'AutoSuggestFromHistory`' or `None` to disable"
324 342 "automatic suggestions. Default is `'AutoSuggestFromHistory`'.",
325 343 allow_none=True,
326 344 ).tag(config=True)
327 345
328 346 def _set_autosuggestions(self, provider):
329 347 if provider is None:
330 348 self.auto_suggest = None
331 349 elif provider == "AutoSuggestFromHistory":
332 350 self.auto_suggest = AutoSuggestFromHistory()
333 351 else:
334 352 raise ValueError("No valid provider.")
335 353 if self.pt_app:
336 354 self.pt_app.auto_suggest = self.auto_suggest
337 355
338 356 @observe("autosuggestions_provider")
339 357 def _autosuggestions_provider_changed(self, change):
340 358 provider = change.new
341 359 self._set_autosuggestions(provider)
342 360
343 361 prompt_includes_vi_mode = Bool(True,
344 362 help="Display the current vi mode (when using vi editing mode)."
345 363 ).tag(config=True)
346 364
347 365 @observe('term_title')
348 366 def init_term_title(self, change=None):
349 367 # Enable or disable the terminal title.
350 368 if self.term_title:
351 369 toggle_set_term_title(True)
352 370 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
353 371 else:
354 372 toggle_set_term_title(False)
355 373
356 374 def restore_term_title(self):
357 375 if self.term_title:
358 376 restore_term_title()
359 377
360 378 def init_display_formatter(self):
361 379 super(TerminalInteractiveShell, self).init_display_formatter()
362 380 # terminal only supports plain text
363 381 self.display_formatter.active_types = ["text/plain"]
364 382
365 383 def init_prompt_toolkit_cli(self):
366 384 if self.simple_prompt:
367 385 # Fall back to plain non-interactive output for tests.
368 386 # This is very limited.
369 387 def prompt():
370 388 prompt_text = "".join(x[1] for x in self.prompts.in_prompt_tokens())
371 389 lines = [input(prompt_text)]
372 390 prompt_continuation = "".join(x[1] for x in self.prompts.continuation_prompt_tokens())
373 391 while self.check_complete('\n'.join(lines))[0] == 'incomplete':
374 392 lines.append( input(prompt_continuation) )
375 393 return '\n'.join(lines)
376 394 self.prompt_for_code = prompt
377 395 return
378 396
379 397 # Set up keyboard shortcuts
380 398 key_bindings = create_ipython_shortcuts(self)
381 399
382 400 # Pre-populate history from IPython's history database
383 401 history = InMemoryHistory()
384 402 last_cell = u""
385 403 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
386 404 include_latest=True):
387 405 # Ignore blank lines and consecutive duplicates
388 406 cell = cell.rstrip()
389 407 if cell and (cell != last_cell):
390 408 history.append_string(cell)
391 409 last_cell = cell
392 410
393 411 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
394 412 self.style = DynamicStyle(lambda: self._style)
395 413
396 414 editing_mode = getattr(EditingMode, self.editing_mode.upper())
397 415
398 416 self.pt_loop = asyncio.new_event_loop()
399 417 self.pt_app = PromptSession(
400 418 auto_suggest=self.auto_suggest,
401 419 editing_mode=editing_mode,
402 420 key_bindings=key_bindings,
403 421 history=history,
404 422 completer=IPythonPTCompleter(shell=self),
405 423 enable_history_search=self.enable_history_search,
406 424 style=self.style,
407 425 include_default_pygments_style=False,
408 426 mouse_support=self.mouse_support,
409 427 enable_open_in_editor=self.extra_open_editor_shortcuts,
410 428 color_depth=self.color_depth,
411 429 tempfile_suffix=".py",
412 430 **self._extra_prompt_options()
413 431 )
414 432
415 433 def _make_style_from_name_or_cls(self, name_or_cls):
416 434 """
417 435 Small wrapper that make an IPython compatible style from a style name
418 436
419 437 We need that to add style for prompt ... etc.
420 438 """
421 439 style_overrides = {}
422 440 if name_or_cls == 'legacy':
423 441 legacy = self.colors.lower()
424 442 if legacy == 'linux':
425 443 style_cls = get_style_by_name('monokai')
426 444 style_overrides = _style_overrides_linux
427 445 elif legacy == 'lightbg':
428 446 style_overrides = _style_overrides_light_bg
429 447 style_cls = get_style_by_name('pastie')
430 448 elif legacy == 'neutral':
431 449 # The default theme needs to be visible on both a dark background
432 450 # and a light background, because we can't tell what the terminal
433 451 # looks like. These tweaks to the default theme help with that.
434 452 style_cls = get_style_by_name('default')
435 453 style_overrides.update({
436 454 Token.Number: '#ansigreen',
437 455 Token.Operator: 'noinherit',
438 456 Token.String: '#ansiyellow',
439 457 Token.Name.Function: '#ansiblue',
440 458 Token.Name.Class: 'bold #ansiblue',
441 459 Token.Name.Namespace: 'bold #ansiblue',
442 460 Token.Name.Variable.Magic: '#ansiblue',
443 461 Token.Prompt: '#ansigreen',
444 462 Token.PromptNum: '#ansibrightgreen bold',
445 463 Token.OutPrompt: '#ansired',
446 464 Token.OutPromptNum: '#ansibrightred bold',
447 465 })
448 466
449 467 # Hack: Due to limited color support on the Windows console
450 468 # the prompt colors will be wrong without this
451 469 if os.name == 'nt':
452 470 style_overrides.update({
453 471 Token.Prompt: '#ansidarkgreen',
454 472 Token.PromptNum: '#ansigreen bold',
455 473 Token.OutPrompt: '#ansidarkred',
456 474 Token.OutPromptNum: '#ansired bold',
457 475 })
458 476 elif legacy =='nocolor':
459 477 style_cls=_NoStyle
460 478 style_overrides = {}
461 479 else :
462 480 raise ValueError('Got unknown colors: ', legacy)
463 481 else :
464 482 if isinstance(name_or_cls, str):
465 483 style_cls = get_style_by_name(name_or_cls)
466 484 else:
467 485 style_cls = name_or_cls
468 486 style_overrides = {
469 487 Token.Prompt: '#ansigreen',
470 488 Token.PromptNum: '#ansibrightgreen bold',
471 489 Token.OutPrompt: '#ansired',
472 490 Token.OutPromptNum: '#ansibrightred bold',
473 491 }
474 492 style_overrides.update(self.highlighting_style_overrides)
475 493 style = merge_styles([
476 494 style_from_pygments_cls(style_cls),
477 495 style_from_pygments_dict(style_overrides),
478 496 ])
479 497
480 498 return style
481 499
482 500 @property
483 501 def pt_complete_style(self):
484 502 return {
485 503 'multicolumn': CompleteStyle.MULTI_COLUMN,
486 504 'column': CompleteStyle.COLUMN,
487 505 'readlinelike': CompleteStyle.READLINE_LIKE,
488 506 }[self.display_completions]
489 507
490 508 @property
491 509 def color_depth(self):
492 510 return (ColorDepth.TRUE_COLOR if self.true_color else None)
493 511
494 512 def _extra_prompt_options(self):
495 513 """
496 514 Return the current layout option for the current Terminal InteractiveShell
497 515 """
498 516 def get_message():
499 517 return PygmentsTokens(self.prompts.in_prompt_tokens())
500 518
501 519 if self.editing_mode == 'emacs':
502 520 # with emacs mode the prompt is (usually) static, so we call only
503 521 # the function once. With VI mode it can toggle between [ins] and
504 522 # [nor] so we can't precompute.
505 523 # here I'm going to favor the default keybinding which almost
506 524 # everybody uses to decrease CPU usage.
507 525 # if we have issues with users with custom Prompts we can see how to
508 526 # work around this.
509 527 get_message = get_message()
510 528
511 529 options = {
512 530 'complete_in_thread': False,
513 531 'lexer':IPythonPTLexer(),
514 532 'reserve_space_for_menu':self.space_for_menu,
515 533 'message': get_message,
516 534 'prompt_continuation': (
517 535 lambda width, lineno, is_soft_wrap:
518 536 PygmentsTokens(self.prompts.continuation_prompt_tokens(width))),
519 537 'multiline': True,
520 538 'complete_style': self.pt_complete_style,
521 539
522 540 # Highlight matching brackets, but only when this setting is
523 541 # enabled, and only when the DEFAULT_BUFFER has the focus.
524 542 'input_processors': [ConditionalProcessor(
525 543 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
526 544 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
527 545 Condition(lambda: self.highlight_matching_brackets))],
528 546 }
529 547 if not PTK3:
530 548 options['inputhook'] = self.inputhook
531 549
532 550 return options
533 551
534 552 def prompt_for_code(self):
535 553 if self.rl_next_input:
536 554 default = self.rl_next_input
537 555 self.rl_next_input = None
538 556 else:
539 557 default = ''
540 558
541 559 # In order to make sure that asyncio code written in the
542 560 # interactive shell doesn't interfere with the prompt, we run the
543 561 # prompt in a different event loop.
544 562 # If we don't do this, people could spawn coroutine with a
545 563 # while/true inside which will freeze the prompt.
546 564
547 565 policy = asyncio.get_event_loop_policy()
548 566 old_loop = get_asyncio_loop()
549 567
550 568 # FIXME: prompt_toolkit is using the deprecated `asyncio.get_event_loop`
551 569 # to get the current event loop.
552 570 # This will probably be replaced by an attribute or input argument,
553 571 # at which point we can stop calling the soon-to-be-deprecated `set_event_loop` here.
554 572 if old_loop is not self.pt_loop:
555 573 policy.set_event_loop(self.pt_loop)
556 574 try:
557 575 with patch_stdout(raw=True):
558 576 text = self.pt_app.prompt(
559 577 default=default,
560 578 **self._extra_prompt_options())
561 579 finally:
562 580 # Restore the original event loop.
563 581 if old_loop is not None and old_loop is not self.pt_loop:
564 582 policy.set_event_loop(old_loop)
565 583
566 584 return text
567 585
568 586 def enable_win_unicode_console(self):
569 587 # Since IPython 7.10 doesn't support python < 3.6 and PEP 528, Python uses the unicode APIs for the Windows
570 588 # console by default, so WUC shouldn't be needed.
571 589 from warnings import warn
572 590 warn("`enable_win_unicode_console` is deprecated since IPython 7.10, does not do anything and will be removed in the future",
573 591 DeprecationWarning,
574 592 stacklevel=2)
575 593
576 594 def init_io(self):
577 595 if sys.platform not in {'win32', 'cli'}:
578 596 return
579 597
580 598 import colorama
581 599 colorama.init()
582 600
583 601 def init_magics(self):
584 602 super(TerminalInteractiveShell, self).init_magics()
585 603 self.register_magics(TerminalMagics)
586 604
587 605 def init_alias(self):
588 606 # The parent class defines aliases that can be safely used with any
589 607 # frontend.
590 608 super(TerminalInteractiveShell, self).init_alias()
591 609
592 610 # Now define aliases that only make sense on the terminal, because they
593 611 # need direct access to the console in a way that we can't emulate in
594 612 # GUI or web frontend
595 613 if os.name == 'posix':
596 614 for cmd in ('clear', 'more', 'less', 'man'):
597 615 self.alias_manager.soft_define_alias(cmd, cmd)
598 616
599 617
600 618 def __init__(self, *args, **kwargs):
601 619 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
602 620 self._set_autosuggestions(self.autosuggestions_provider)
603 621 self.init_prompt_toolkit_cli()
604 622 self.init_term_title()
605 623 self.keep_running = True
606 624 self._set_formatter(self.autoformatter)
607 625
608 626
609 627 def ask_exit(self):
610 628 self.keep_running = False
611 629
612 630 rl_next_input = None
613 631
614 632 def interact(self):
615 633 self.keep_running = True
616 634 while self.keep_running:
617 635 print(self.separate_in, end='')
618 636
619 637 try:
620 638 code = self.prompt_for_code()
621 639 except EOFError:
622 640 if (not self.confirm_exit) \
623 641 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
624 642 self.ask_exit()
625 643
626 644 else:
627 645 if code:
628 646 self.run_cell(code, store_history=True)
629 647
630 648 def mainloop(self):
631 649 # An extra layer of protection in case someone mashing Ctrl-C breaks
632 650 # out of our internal code.
633 651 while True:
634 652 try:
635 653 self.interact()
636 654 break
637 655 except KeyboardInterrupt as e:
638 656 print("\n%s escaped interact()\n" % type(e).__name__)
639 657 finally:
640 658 # An interrupt during the eventloop will mess up the
641 659 # internal state of the prompt_toolkit library.
642 660 # Stopping the eventloop fixes this, see
643 661 # https://github.com/ipython/ipython/pull/9867
644 662 if hasattr(self, '_eventloop'):
645 663 self._eventloop.stop()
646 664
647 665 self.restore_term_title()
648 666
649 667 # try to call some at-exit operation optimistically as some things can't
650 668 # be done during interpreter shutdown. this is technically inaccurate as
651 669 # this make mainlool not re-callable, but that should be a rare if not
652 670 # in existent use case.
653 671
654 672 self._atexit_once()
655 673
656 674
657 675 _inputhook = None
658 676 def inputhook(self, context):
659 677 if self._inputhook is not None:
660 678 self._inputhook(context)
661 679
662 680 active_eventloop = None
663 681 def enable_gui(self, gui=None):
664 682 if gui and (gui != 'inline') :
665 683 self.active_eventloop, self._inputhook =\
666 684 get_inputhook_name_and_func(gui)
667 685 else:
668 686 self.active_eventloop = self._inputhook = None
669 687
670 688 # For prompt_toolkit 3.0. We have to create an asyncio event loop with
671 689 # this inputhook.
672 690 if PTK3:
673 691 import asyncio
674 692 from prompt_toolkit.eventloop import new_eventloop_with_inputhook
675 693
676 694 if gui == 'asyncio':
677 695 # When we integrate the asyncio event loop, run the UI in the
678 696 # same event loop as the rest of the code. don't use an actual
679 697 # input hook. (Asyncio is not made for nesting event loops.)
680 698 self.pt_loop = get_asyncio_loop()
681 699
682 700 elif self._inputhook:
683 701 # If an inputhook was set, create a new asyncio event loop with
684 702 # this inputhook for the prompt.
685 703 self.pt_loop = new_eventloop_with_inputhook(self._inputhook)
686 704 else:
687 705 # When there's no inputhook, run the prompt in a separate
688 706 # asyncio event loop.
689 707 self.pt_loop = asyncio.new_event_loop()
690 708
691 709 # Run !system commands directly, not through pipes, so terminal programs
692 710 # work correctly.
693 711 system = InteractiveShell.system_raw
694 712
695 713 def auto_rewrite_input(self, cmd):
696 714 """Overridden from the parent class to use fancy rewriting prompt"""
697 715 if not self.show_rewritten_input:
698 716 return
699 717
700 718 tokens = self.prompts.rewrite_prompt_tokens()
701 719 if self.pt_app:
702 720 print_formatted_text(PygmentsTokens(tokens), end='',
703 721 style=self.pt_app.app.style)
704 722 print(cmd)
705 723 else:
706 724 prompt = ''.join(s for t, s in tokens)
707 725 print(prompt, cmd, sep='')
708 726
709 727 _prompts_before = None
710 728 def switch_doctest_mode(self, mode):
711 729 """Switch prompts to classic for %doctest_mode"""
712 730 if mode:
713 731 self._prompts_before = self.prompts
714 732 self.prompts = ClassicPrompts(self)
715 733 elif self._prompts_before:
716 734 self.prompts = self._prompts_before
717 735 self._prompts_before = None
718 736 # self._update_layout()
719 737
720 738
721 739 InteractiveShellABC.register(TerminalInteractiveShell)
722 740
723 741 if __name__ == '__main__':
724 742 TerminalInteractiveShell.instance().interact()
General Comments 0
You need to be logged in to leave comments. Login now