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