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