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