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