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