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