##// END OF EJS Templates
Merge pull request #13475 from asteppke/master...
Matthias Bussonnier -
r27460:97e3d9ec merge
parent child Browse files
Show More
@@ -1,700 +1,724 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 autosuggestions_provider = Unicode(
321 "AutoSuggestFromHistory",
322 help="Specifies from which source automatic suggestions are provided. "
323 "Can be set to `'AutoSuggestFromHistory`' or `None` to disable"
324 "automatic suggestions. Default is `'AutoSuggestFromHistory`'.",
325 allow_none=True,
326 ).tag(config=True)
327
328 def _set_autosuggestions(self, provider):
329 if provider is None:
330 self.auto_suggest = None
331 elif provider == "AutoSuggestFromHistory":
332 self.auto_suggest = AutoSuggestFromHistory()
333 else:
334 raise ValueError("No valid provider.")
335 if self.pt_app:
336 self.pt_app.auto_suggest = self.auto_suggest
337
338 @observe("autosuggestions_provider")
339 def _autosuggestions_provider_changed(self, change):
340 provider = change.new
341 self._set_autosuggestions(provider)
342
320 343 prompt_includes_vi_mode = Bool(True,
321 344 help="Display the current vi mode (when using vi editing mode)."
322 345 ).tag(config=True)
323 346
324 347 @observe('term_title')
325 348 def init_term_title(self, change=None):
326 349 # Enable or disable the terminal title.
327 350 if self.term_title:
328 351 toggle_set_term_title(True)
329 352 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
330 353 else:
331 354 toggle_set_term_title(False)
332 355
333 356 def restore_term_title(self):
334 357 if self.term_title:
335 358 restore_term_title()
336 359
337 360 def init_display_formatter(self):
338 361 super(TerminalInteractiveShell, self).init_display_formatter()
339 362 # terminal only supports plain text
340 363 self.display_formatter.active_types = ["text/plain"]
341 364
342 365 def init_prompt_toolkit_cli(self):
343 366 if self.simple_prompt:
344 367 # Fall back to plain non-interactive output for tests.
345 368 # This is very limited.
346 369 def prompt():
347 370 prompt_text = "".join(x[1] for x in self.prompts.in_prompt_tokens())
348 371 lines = [input(prompt_text)]
349 372 prompt_continuation = "".join(x[1] for x in self.prompts.continuation_prompt_tokens())
350 373 while self.check_complete('\n'.join(lines))[0] == 'incomplete':
351 374 lines.append( input(prompt_continuation) )
352 375 return '\n'.join(lines)
353 376 self.prompt_for_code = prompt
354 377 return
355 378
356 379 # Set up keyboard shortcuts
357 380 key_bindings = create_ipython_shortcuts(self)
358 381
359 382 # Pre-populate history from IPython's history database
360 383 history = InMemoryHistory()
361 384 last_cell = u""
362 385 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
363 386 include_latest=True):
364 387 # Ignore blank lines and consecutive duplicates
365 388 cell = cell.rstrip()
366 389 if cell and (cell != last_cell):
367 390 history.append_string(cell)
368 391 last_cell = cell
369 392
370 393 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
371 394 self.style = DynamicStyle(lambda: self._style)
372 395
373 396 editing_mode = getattr(EditingMode, self.editing_mode.upper())
374 397
375 398 self.pt_loop = asyncio.new_event_loop()
376 399 self.pt_app = PromptSession(
377 auto_suggest=AutoSuggestFromHistory(),
400 auto_suggest=self.auto_suggest,
378 401 editing_mode=editing_mode,
379 402 key_bindings=key_bindings,
380 403 history=history,
381 404 completer=IPythonPTCompleter(shell=self),
382 405 enable_history_search=self.enable_history_search,
383 406 style=self.style,
384 407 include_default_pygments_style=False,
385 408 mouse_support=self.mouse_support,
386 409 enable_open_in_editor=self.extra_open_editor_shortcuts,
387 410 color_depth=self.color_depth,
388 411 tempfile_suffix=".py",
389 412 **self._extra_prompt_options()
390 413 )
391 414
392 415 def _make_style_from_name_or_cls(self, name_or_cls):
393 416 """
394 417 Small wrapper that make an IPython compatible style from a style name
395 418
396 419 We need that to add style for prompt ... etc.
397 420 """
398 421 style_overrides = {}
399 422 if name_or_cls == 'legacy':
400 423 legacy = self.colors.lower()
401 424 if legacy == 'linux':
402 425 style_cls = get_style_by_name('monokai')
403 426 style_overrides = _style_overrides_linux
404 427 elif legacy == 'lightbg':
405 428 style_overrides = _style_overrides_light_bg
406 429 style_cls = get_style_by_name('pastie')
407 430 elif legacy == 'neutral':
408 431 # The default theme needs to be visible on both a dark background
409 432 # and a light background, because we can't tell what the terminal
410 433 # looks like. These tweaks to the default theme help with that.
411 434 style_cls = get_style_by_name('default')
412 435 style_overrides.update({
413 436 Token.Number: '#ansigreen',
414 437 Token.Operator: 'noinherit',
415 438 Token.String: '#ansiyellow',
416 439 Token.Name.Function: '#ansiblue',
417 440 Token.Name.Class: 'bold #ansiblue',
418 441 Token.Name.Namespace: 'bold #ansiblue',
419 442 Token.Name.Variable.Magic: '#ansiblue',
420 443 Token.Prompt: '#ansigreen',
421 444 Token.PromptNum: '#ansibrightgreen bold',
422 445 Token.OutPrompt: '#ansired',
423 446 Token.OutPromptNum: '#ansibrightred bold',
424 447 })
425 448
426 449 # Hack: Due to limited color support on the Windows console
427 450 # the prompt colors will be wrong without this
428 451 if os.name == 'nt':
429 452 style_overrides.update({
430 453 Token.Prompt: '#ansidarkgreen',
431 454 Token.PromptNum: '#ansigreen bold',
432 455 Token.OutPrompt: '#ansidarkred',
433 456 Token.OutPromptNum: '#ansired bold',
434 457 })
435 458 elif legacy =='nocolor':
436 459 style_cls=_NoStyle
437 460 style_overrides = {}
438 461 else :
439 462 raise ValueError('Got unknown colors: ', legacy)
440 463 else :
441 464 if isinstance(name_or_cls, str):
442 465 style_cls = get_style_by_name(name_or_cls)
443 466 else:
444 467 style_cls = name_or_cls
445 468 style_overrides = {
446 469 Token.Prompt: '#ansigreen',
447 470 Token.PromptNum: '#ansibrightgreen bold',
448 471 Token.OutPrompt: '#ansired',
449 472 Token.OutPromptNum: '#ansibrightred bold',
450 473 }
451 474 style_overrides.update(self.highlighting_style_overrides)
452 475 style = merge_styles([
453 476 style_from_pygments_cls(style_cls),
454 477 style_from_pygments_dict(style_overrides),
455 478 ])
456 479
457 480 return style
458 481
459 482 @property
460 483 def pt_complete_style(self):
461 484 return {
462 485 'multicolumn': CompleteStyle.MULTI_COLUMN,
463 486 'column': CompleteStyle.COLUMN,
464 487 'readlinelike': CompleteStyle.READLINE_LIKE,
465 488 }[self.display_completions]
466 489
467 490 @property
468 491 def color_depth(self):
469 492 return (ColorDepth.TRUE_COLOR if self.true_color else None)
470 493
471 494 def _extra_prompt_options(self):
472 495 """
473 496 Return the current layout option for the current Terminal InteractiveShell
474 497 """
475 498 def get_message():
476 499 return PygmentsTokens(self.prompts.in_prompt_tokens())
477 500
478 501 if self.editing_mode == 'emacs':
479 502 # with emacs mode the prompt is (usually) static, so we call only
480 503 # the function once. With VI mode it can toggle between [ins] and
481 504 # [nor] so we can't precompute.
482 505 # here I'm going to favor the default keybinding which almost
483 506 # everybody uses to decrease CPU usage.
484 507 # if we have issues with users with custom Prompts we can see how to
485 508 # work around this.
486 509 get_message = get_message()
487 510
488 511 options = {
489 512 'complete_in_thread': False,
490 513 'lexer':IPythonPTLexer(),
491 514 'reserve_space_for_menu':self.space_for_menu,
492 515 'message': get_message,
493 516 'prompt_continuation': (
494 517 lambda width, lineno, is_soft_wrap:
495 518 PygmentsTokens(self.prompts.continuation_prompt_tokens(width))),
496 519 'multiline': True,
497 520 'complete_style': self.pt_complete_style,
498 521
499 522 # Highlight matching brackets, but only when this setting is
500 523 # enabled, and only when the DEFAULT_BUFFER has the focus.
501 524 'input_processors': [ConditionalProcessor(
502 525 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
503 526 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
504 527 Condition(lambda: self.highlight_matching_brackets))],
505 528 }
506 529 if not PTK3:
507 530 options['inputhook'] = self.inputhook
508 531
509 532 return options
510 533
511 534 def prompt_for_code(self):
512 535 if self.rl_next_input:
513 536 default = self.rl_next_input
514 537 self.rl_next_input = None
515 538 else:
516 539 default = ''
517 540
518 541 # In order to make sure that asyncio code written in the
519 542 # interactive shell doesn't interfere with the prompt, we run the
520 543 # prompt in a different event loop.
521 544 # If we don't do this, people could spawn coroutine with a
522 545 # while/true inside which will freeze the prompt.
523 546
524 547 policy = asyncio.get_event_loop_policy()
525 548 old_loop = get_asyncio_loop()
526 549
527 550 # FIXME: prompt_toolkit is using the deprecated `asyncio.get_event_loop`
528 551 # to get the current event loop.
529 552 # This will probably be replaced by an attribute or input argument,
530 553 # at which point we can stop calling the soon-to-be-deprecated `set_event_loop` here.
531 554 if old_loop is not self.pt_loop:
532 555 policy.set_event_loop(self.pt_loop)
533 556 try:
534 557 with patch_stdout(raw=True):
535 558 text = self.pt_app.prompt(
536 559 default=default,
537 560 **self._extra_prompt_options())
538 561 finally:
539 562 # Restore the original event loop.
540 563 if old_loop is not None and old_loop is not self.pt_loop:
541 564 policy.set_event_loop(old_loop)
542 565
543 566 return text
544 567
545 568 def enable_win_unicode_console(self):
546 569 # Since IPython 7.10 doesn't support python < 3.6 and PEP 528, Python uses the unicode APIs for the Windows
547 570 # console by default, so WUC shouldn't be needed.
548 571 from warnings import warn
549 572 warn("`enable_win_unicode_console` is deprecated since IPython 7.10, does not do anything and will be removed in the future",
550 573 DeprecationWarning,
551 574 stacklevel=2)
552 575
553 576 def init_io(self):
554 577 if sys.platform not in {'win32', 'cli'}:
555 578 return
556 579
557 580 import colorama
558 581 colorama.init()
559 582
560 583 def init_magics(self):
561 584 super(TerminalInteractiveShell, self).init_magics()
562 585 self.register_magics(TerminalMagics)
563 586
564 587 def init_alias(self):
565 588 # The parent class defines aliases that can be safely used with any
566 589 # frontend.
567 590 super(TerminalInteractiveShell, self).init_alias()
568 591
569 592 # Now define aliases that only make sense on the terminal, because they
570 593 # need direct access to the console in a way that we can't emulate in
571 594 # GUI or web frontend
572 595 if os.name == 'posix':
573 596 for cmd in ('clear', 'more', 'less', 'man'):
574 597 self.alias_manager.soft_define_alias(cmd, cmd)
575 598
576 599
577 600 def __init__(self, *args, **kwargs):
578 601 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
602 self._set_autosuggestions(self.autosuggestions_provider)
579 603 self.init_prompt_toolkit_cli()
580 604 self.init_term_title()
581 605 self.keep_running = True
582 606 self._set_formatter(self.autoformatter)
583 607
584 608
585 609 def ask_exit(self):
586 610 self.keep_running = False
587 611
588 612 rl_next_input = None
589 613
590 614 def interact(self):
591 615 self.keep_running = True
592 616 while self.keep_running:
593 617 print(self.separate_in, end='')
594 618
595 619 try:
596 620 code = self.prompt_for_code()
597 621 except EOFError:
598 622 if (not self.confirm_exit) \
599 623 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
600 624 self.ask_exit()
601 625
602 626 else:
603 627 if code:
604 628 self.run_cell(code, store_history=True)
605 629
606 630 def mainloop(self):
607 631 # An extra layer of protection in case someone mashing Ctrl-C breaks
608 632 # out of our internal code.
609 633 while True:
610 634 try:
611 635 self.interact()
612 636 break
613 637 except KeyboardInterrupt as e:
614 638 print("\n%s escaped interact()\n" % type(e).__name__)
615 639 finally:
616 640 # An interrupt during the eventloop will mess up the
617 641 # internal state of the prompt_toolkit library.
618 642 # Stopping the eventloop fixes this, see
619 643 # https://github.com/ipython/ipython/pull/9867
620 644 if hasattr(self, '_eventloop'):
621 645 self._eventloop.stop()
622 646
623 647 self.restore_term_title()
624 648
625 649 # try to call some at-exit operation optimistically as some things can't
626 650 # be done during interpreter shutdown. this is technically inaccurate as
627 651 # this make mainlool not re-callable, but that should be a rare if not
628 652 # in existent use case.
629 653
630 654 self._atexit_once()
631 655
632 656
633 657 _inputhook = None
634 658 def inputhook(self, context):
635 659 if self._inputhook is not None:
636 660 self._inputhook(context)
637 661
638 662 active_eventloop = None
639 663 def enable_gui(self, gui=None):
640 664 if gui and (gui != 'inline') :
641 665 self.active_eventloop, self._inputhook =\
642 666 get_inputhook_name_and_func(gui)
643 667 else:
644 668 self.active_eventloop = self._inputhook = None
645 669
646 670 # For prompt_toolkit 3.0. We have to create an asyncio event loop with
647 671 # this inputhook.
648 672 if PTK3:
649 673 import asyncio
650 674 from prompt_toolkit.eventloop import new_eventloop_with_inputhook
651 675
652 676 if gui == 'asyncio':
653 677 # When we integrate the asyncio event loop, run the UI in the
654 678 # same event loop as the rest of the code. don't use an actual
655 679 # input hook. (Asyncio is not made for nesting event loops.)
656 680 self.pt_loop = get_asyncio_loop()
657 681
658 682 elif self._inputhook:
659 683 # If an inputhook was set, create a new asyncio event loop with
660 684 # this inputhook for the prompt.
661 685 self.pt_loop = new_eventloop_with_inputhook(self._inputhook)
662 686 else:
663 687 # When there's no inputhook, run the prompt in a separate
664 688 # asyncio event loop.
665 689 self.pt_loop = asyncio.new_event_loop()
666 690
667 691 # Run !system commands directly, not through pipes, so terminal programs
668 692 # work correctly.
669 693 system = InteractiveShell.system_raw
670 694
671 695 def auto_rewrite_input(self, cmd):
672 696 """Overridden from the parent class to use fancy rewriting prompt"""
673 697 if not self.show_rewritten_input:
674 698 return
675 699
676 700 tokens = self.prompts.rewrite_prompt_tokens()
677 701 if self.pt_app:
678 702 print_formatted_text(PygmentsTokens(tokens), end='',
679 703 style=self.pt_app.app.style)
680 704 print(cmd)
681 705 else:
682 706 prompt = ''.join(s for t, s in tokens)
683 707 print(prompt, cmd, sep='')
684 708
685 709 _prompts_before = None
686 710 def switch_doctest_mode(self, mode):
687 711 """Switch prompts to classic for %doctest_mode"""
688 712 if mode:
689 713 self._prompts_before = self.prompts
690 714 self.prompts = ClassicPrompts(self)
691 715 elif self._prompts_before:
692 716 self.prompts = self._prompts_before
693 717 self._prompts_before = None
694 718 # self._update_layout()
695 719
696 720
697 721 InteractiveShellABC.register(TerminalInteractiveShell)
698 722
699 723 if __name__ == '__main__':
700 724 TerminalInteractiveShell.instance().interact()
General Comments 0
You need to be logged in to leave comments. Login now