##// END OF EJS Templates
Merge pull request #11973 from jonathanslenders/run-prompt-in-different-asyncio-loop...
Matthias Bussonnier -
r25280:e9d59e53 merge
parent child Browse files
Show More
@@ -1,610 +1,623
1 1 """IPython terminal interface using prompt_toolkit"""
2 2
3 import asyncio
3 4 import os
4 5 import sys
5 6 import warnings
6 7 from warnings import warn
7 8
8 9 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
9 10 from IPython.utils import io
10 11 from IPython.utils.py3compat import input
11 12 from IPython.utils.terminal import toggle_set_term_title, set_term_title, restore_term_title
12 13 from IPython.utils.process import abbrev_cwd
13 14 from traitlets import (
14 15 Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum, Union,
15 16 Any, validate
16 17 )
17 18
18 19 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
19 20 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
20 21 from prompt_toolkit.formatted_text import PygmentsTokens
21 22 from prompt_toolkit.history import InMemoryHistory
22 23 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
23 24 from prompt_toolkit.output import ColorDepth
24 25 from prompt_toolkit.patch_stdout import patch_stdout
25 26 from prompt_toolkit.shortcuts import PromptSession, CompleteStyle, print_formatted_text
26 27 from prompt_toolkit.styles import DynamicStyle, merge_styles
27 28 from prompt_toolkit.styles.pygments import style_from_pygments_cls, style_from_pygments_dict
28 29 from prompt_toolkit import __version__ as ptk_version
29 30
30 31 from pygments.styles import get_style_by_name
31 32 from pygments.style import Style
32 33 from pygments.token import Token
33 34
34 35 from .debugger import TerminalPdb, Pdb
35 36 from .magics import TerminalMagics
36 37 from .pt_inputhooks import get_inputhook_name_and_func
37 38 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
38 39 from .ptutils import IPythonPTCompleter, IPythonPTLexer
39 40 from .shortcuts import create_ipython_shortcuts
40 41
41 42 DISPLAY_BANNER_DEPRECATED = object()
42 43 PTK3 = ptk_version.startswith('3.')
43 44
44 45
45 46 class _NoStyle(Style): pass
46 47
47 48
48 49
49 50 _style_overrides_light_bg = {
50 51 Token.Prompt: '#0000ff',
51 52 Token.PromptNum: '#0000ee bold',
52 53 Token.OutPrompt: '#cc0000',
53 54 Token.OutPromptNum: '#bb0000 bold',
54 55 }
55 56
56 57 _style_overrides_linux = {
57 58 Token.Prompt: '#00cc00',
58 59 Token.PromptNum: '#00bb00 bold',
59 60 Token.OutPrompt: '#cc0000',
60 61 Token.OutPromptNum: '#bb0000 bold',
61 62 }
62 63
63 64 def get_default_editor():
64 65 try:
65 66 return os.environ['EDITOR']
66 67 except KeyError:
67 68 pass
68 69 except UnicodeError:
69 70 warn("$EDITOR environment variable is not pure ASCII. Using platform "
70 71 "default editor.")
71 72
72 73 if os.name == 'posix':
73 74 return 'vi' # the only one guaranteed to be there!
74 75 else:
75 76 return 'notepad' # same in Windows!
76 77
77 78 # conservatively check for tty
78 79 # overridden streams can result in things like:
79 80 # - sys.stdin = None
80 81 # - no isatty method
81 82 for _name in ('stdin', 'stdout', 'stderr'):
82 83 _stream = getattr(sys, _name)
83 84 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
84 85 _is_tty = False
85 86 break
86 87 else:
87 88 _is_tty = True
88 89
89 90
90 91 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
91 92
92 93 def black_reformat_handler(text_before_cursor):
93 94 import black
94 95 formatted_text = black.format_str(text_before_cursor, mode=black.FileMode())
95 96 if not text_before_cursor.endswith('\n') and formatted_text.endswith('\n'):
96 97 formatted_text = formatted_text[:-1]
97 98 return formatted_text
98 99
99 100
100 101 class TerminalInteractiveShell(InteractiveShell):
101 102 mime_renderers = Dict().tag(config=True)
102 103
103 104 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
104 105 'to reserve for the completion menu'
105 106 ).tag(config=True)
106 107
107 108 pt_app = None
108 109 debugger_history = None
109 110
110 111 simple_prompt = Bool(_use_simple_prompt,
111 112 help="""Use `raw_input` for the REPL, without completion and prompt colors.
112 113
113 114 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
114 115 IPython own testing machinery, and emacs inferior-shell integration through elpy.
115 116
116 117 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
117 118 environment variable is set, or the current terminal is not a tty."""
118 119 ).tag(config=True)
119 120
120 121 @property
121 122 def debugger_cls(self):
122 123 return Pdb if self.simple_prompt else TerminalPdb
123 124
124 125 confirm_exit = Bool(True,
125 126 help="""
126 127 Set to confirm when you try to exit IPython with an EOF (Control-D
127 128 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
128 129 you can force a direct exit without any confirmation.""",
129 130 ).tag(config=True)
130 131
131 132 editing_mode = Unicode('emacs',
132 133 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
133 134 ).tag(config=True)
134 135
135 136 autoformatter = Unicode(None,
136 137 help="Autoformatter to reformat Terminal code. Can be `'black'` or `None`",
137 138 allow_none=True
138 139 ).tag(config=True)
139 140
140 141 mouse_support = Bool(False,
141 142 help="Enable mouse support in the prompt\n(Note: prevents selecting text with the mouse)"
142 143 ).tag(config=True)
143 144
144 145 # We don't load the list of styles for the help string, because loading
145 146 # Pygments plugins takes time and can cause unexpected errors.
146 147 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
147 148 help="""The name or class of a Pygments style to use for syntax
148 149 highlighting. To see available styles, run `pygmentize -L styles`."""
149 150 ).tag(config=True)
150 151
151 152 @validate('editing_mode')
152 153 def _validate_editing_mode(self, proposal):
153 154 if proposal['value'].lower() == 'vim':
154 155 proposal['value']= 'vi'
155 156 elif proposal['value'].lower() == 'default':
156 157 proposal['value']= 'emacs'
157 158
158 159 if hasattr(EditingMode, proposal['value'].upper()):
159 160 return proposal['value'].lower()
160 161
161 162 return self.editing_mode
162 163
163 164
164 165 @observe('editing_mode')
165 166 def _editing_mode(self, change):
166 167 u_mode = change.new.upper()
167 168 if self.pt_app:
168 169 self.pt_app.editing_mode = u_mode
169 170
170 171 @observe('autoformatter')
171 172 def _autoformatter_changed(self, change):
172 173 formatter = change.new
173 174 if formatter is None:
174 175 self.reformat_handler = lambda x:x
175 176 elif formatter == 'black':
176 177 self.reformat_handler = black_reformat_handler
177 178 else:
178 179 raise ValueError
179 180
180 181 @observe('highlighting_style')
181 182 @observe('colors')
182 183 def _highlighting_style_changed(self, change):
183 184 self.refresh_style()
184 185
185 186 def refresh_style(self):
186 187 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
187 188
188 189
189 190 highlighting_style_overrides = Dict(
190 191 help="Override highlighting format for specific tokens"
191 192 ).tag(config=True)
192 193
193 194 true_color = Bool(False,
194 195 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
195 196 "If your terminal supports true color, the following command "
196 197 "should print 'TRUECOLOR' in orange: "
197 198 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
198 199 ).tag(config=True)
199 200
200 201 editor = Unicode(get_default_editor(),
201 202 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
202 203 ).tag(config=True)
203 204
204 205 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
205 206
206 207 prompts = Instance(Prompts)
207 208
208 209 @default('prompts')
209 210 def _prompts_default(self):
210 211 return self.prompts_class(self)
211 212
212 213 # @observe('prompts')
213 214 # def _(self, change):
214 215 # self._update_layout()
215 216
216 217 @default('displayhook_class')
217 218 def _displayhook_class_default(self):
218 219 return RichPromptDisplayHook
219 220
220 221 term_title = Bool(True,
221 222 help="Automatically set the terminal title"
222 223 ).tag(config=True)
223 224
224 225 term_title_format = Unicode("IPython: {cwd}",
225 226 help="Customize the terminal title format. This is a python format string. " +
226 227 "Available substitutions are: {cwd}."
227 228 ).tag(config=True)
228 229
229 230 display_completions = Enum(('column', 'multicolumn','readlinelike'),
230 231 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
231 232 "'readlinelike'. These options are for `prompt_toolkit`, see "
232 233 "`prompt_toolkit` documentation for more information."
233 234 ),
234 235 default_value='multicolumn').tag(config=True)
235 236
236 237 highlight_matching_brackets = Bool(True,
237 238 help="Highlight matching brackets.",
238 239 ).tag(config=True)
239 240
240 241 extra_open_editor_shortcuts = Bool(False,
241 242 help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
242 243 "This is in addition to the F2 binding, which is always enabled."
243 244 ).tag(config=True)
244 245
245 246 handle_return = Any(None,
246 247 help="Provide an alternative handler to be called when the user presses "
247 248 "Return. This is an advanced option intended for debugging, which "
248 249 "may be changed or removed in later releases."
249 250 ).tag(config=True)
250 251
251 252 enable_history_search = Bool(True,
252 253 help="Allows to enable/disable the prompt toolkit history search"
253 254 ).tag(config=True)
254 255
255 256 prompt_includes_vi_mode = Bool(True,
256 257 help="Display the current vi mode (when using vi editing mode)."
257 258 ).tag(config=True)
258 259
259 260 @observe('term_title')
260 261 def init_term_title(self, change=None):
261 262 # Enable or disable the terminal title.
262 263 if self.term_title:
263 264 toggle_set_term_title(True)
264 265 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
265 266 else:
266 267 toggle_set_term_title(False)
267 268
268 269 def restore_term_title(self):
269 270 if self.term_title:
270 271 restore_term_title()
271 272
272 273 def init_display_formatter(self):
273 274 super(TerminalInteractiveShell, self).init_display_formatter()
274 275 # terminal only supports plain text
275 276 self.display_formatter.active_types = ['text/plain']
276 277 # disable `_ipython_display_`
277 278 self.display_formatter.ipython_display_formatter.enabled = False
278 279
279 280 def init_prompt_toolkit_cli(self):
280 281 if self.simple_prompt:
281 282 # Fall back to plain non-interactive output for tests.
282 283 # This is very limited.
283 284 def prompt():
284 285 prompt_text = "".join(x[1] for x in self.prompts.in_prompt_tokens())
285 286 lines = [input(prompt_text)]
286 287 prompt_continuation = "".join(x[1] for x in self.prompts.continuation_prompt_tokens())
287 288 while self.check_complete('\n'.join(lines))[0] == 'incomplete':
288 289 lines.append( input(prompt_continuation) )
289 290 return '\n'.join(lines)
290 291 self.prompt_for_code = prompt
291 292 return
292 293
293 294 # Set up keyboard shortcuts
294 295 key_bindings = create_ipython_shortcuts(self)
295 296
296 297 # Pre-populate history from IPython's history database
297 298 history = InMemoryHistory()
298 299 last_cell = u""
299 300 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
300 301 include_latest=True):
301 302 # Ignore blank lines and consecutive duplicates
302 303 cell = cell.rstrip()
303 304 if cell and (cell != last_cell):
304 305 history.append_string(cell)
305 306 last_cell = cell
306 307
307 308 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
308 309 self.style = DynamicStyle(lambda: self._style)
309 310
310 311 editing_mode = getattr(EditingMode, self.editing_mode.upper())
311 312
313 self.pt_loop = asyncio.new_event_loop()
312 314 self.pt_app = PromptSession(
313 315 editing_mode=editing_mode,
314 316 key_bindings=key_bindings,
315 317 history=history,
316 318 completer=IPythonPTCompleter(shell=self),
317 319 enable_history_search = self.enable_history_search,
318 320 style=self.style,
319 321 include_default_pygments_style=False,
320 322 mouse_support=self.mouse_support,
321 323 enable_open_in_editor=self.extra_open_editor_shortcuts,
322 324 color_depth=self.color_depth,
323 325 **self._extra_prompt_options())
324 326
325 327 def _make_style_from_name_or_cls(self, name_or_cls):
326 328 """
327 329 Small wrapper that make an IPython compatible style from a style name
328 330
329 331 We need that to add style for prompt ... etc.
330 332 """
331 333 style_overrides = {}
332 334 if name_or_cls == 'legacy':
333 335 legacy = self.colors.lower()
334 336 if legacy == 'linux':
335 337 style_cls = get_style_by_name('monokai')
336 338 style_overrides = _style_overrides_linux
337 339 elif legacy == 'lightbg':
338 340 style_overrides = _style_overrides_light_bg
339 341 style_cls = get_style_by_name('pastie')
340 342 elif legacy == 'neutral':
341 343 # The default theme needs to be visible on both a dark background
342 344 # and a light background, because we can't tell what the terminal
343 345 # looks like. These tweaks to the default theme help with that.
344 346 style_cls = get_style_by_name('default')
345 347 style_overrides.update({
346 348 Token.Number: '#007700',
347 349 Token.Operator: 'noinherit',
348 350 Token.String: '#BB6622',
349 351 Token.Name.Function: '#2080D0',
350 352 Token.Name.Class: 'bold #2080D0',
351 353 Token.Name.Namespace: 'bold #2080D0',
352 354 Token.Prompt: '#009900',
353 355 Token.PromptNum: '#ansibrightgreen bold',
354 356 Token.OutPrompt: '#990000',
355 357 Token.OutPromptNum: '#ansibrightred bold',
356 358 })
357 359
358 360 # Hack: Due to limited color support on the Windows console
359 361 # the prompt colors will be wrong without this
360 362 if os.name == 'nt':
361 363 style_overrides.update({
362 364 Token.Prompt: '#ansidarkgreen',
363 365 Token.PromptNum: '#ansigreen bold',
364 366 Token.OutPrompt: '#ansidarkred',
365 367 Token.OutPromptNum: '#ansired bold',
366 368 })
367 369 elif legacy =='nocolor':
368 370 style_cls=_NoStyle
369 371 style_overrides = {}
370 372 else :
371 373 raise ValueError('Got unknown colors: ', legacy)
372 374 else :
373 375 if isinstance(name_or_cls, str):
374 376 style_cls = get_style_by_name(name_or_cls)
375 377 else:
376 378 style_cls = name_or_cls
377 379 style_overrides = {
378 380 Token.Prompt: '#009900',
379 381 Token.PromptNum: '#ansibrightgreen bold',
380 382 Token.OutPrompt: '#990000',
381 383 Token.OutPromptNum: '#ansibrightred bold',
382 384 }
383 385 style_overrides.update(self.highlighting_style_overrides)
384 386 style = merge_styles([
385 387 style_from_pygments_cls(style_cls),
386 388 style_from_pygments_dict(style_overrides),
387 389 ])
388 390
389 391 return style
390 392
391 393 @property
392 394 def pt_complete_style(self):
393 395 return {
394 396 'multicolumn': CompleteStyle.MULTI_COLUMN,
395 397 'column': CompleteStyle.COLUMN,
396 398 'readlinelike': CompleteStyle.READLINE_LIKE,
397 399 }[self.display_completions]
398 400
399 401 @property
400 402 def color_depth(self):
401 403 return (ColorDepth.TRUE_COLOR if self.true_color else None)
402 404
403 405 def _extra_prompt_options(self):
404 406 """
405 407 Return the current layout option for the current Terminal InteractiveShell
406 408 """
407 409 def get_message():
408 410 return PygmentsTokens(self.prompts.in_prompt_tokens())
409 411
410 412 if self.editing_mode == 'emacs':
411 413 # with emacs mode the prompt is (usually) static, so we call only
412 414 # the function once. With VI mode it can toggle between [ins] and
413 415 # [nor] so we can't precompute.
414 416 # here I'm going to favor the default keybinding which almost
415 417 # everybody uses to decrease CPU usage.
416 418 # if we have issues with users with custom Prompts we can see how to
417 419 # work around this.
418 420 get_message = get_message()
419 421
420 422 options = {
421 423 'complete_in_thread': False,
422 424 'lexer':IPythonPTLexer(),
423 425 'reserve_space_for_menu':self.space_for_menu,
424 426 'message': get_message,
425 427 'prompt_continuation': (
426 428 lambda width, lineno, is_soft_wrap:
427 429 PygmentsTokens(self.prompts.continuation_prompt_tokens(width))),
428 430 'multiline': True,
429 431 'complete_style': self.pt_complete_style,
430 432
431 433 # Highlight matching brackets, but only when this setting is
432 434 # enabled, and only when the DEFAULT_BUFFER has the focus.
433 435 'input_processors': [ConditionalProcessor(
434 436 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
435 437 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
436 438 Condition(lambda: self.highlight_matching_brackets))],
437 439 }
438 440 if not PTK3:
439 441 options['inputhook'] = self.inputhook
440 442
441 443 return options
442 444
443 445 def prompt_for_code(self):
444 446 if self.rl_next_input:
445 447 default = self.rl_next_input
446 448 self.rl_next_input = None
447 449 else:
448 450 default = ''
449 451
450 452 with patch_stdout(raw=True):
451 text = self.pt_app.prompt(
452 default=default,
453 # pre_run=self.pre_prompt,# reset_current_buffer=True,
454 **self._extra_prompt_options())
453 # In order to make sure that asyncio code written in the
454 # interactive shell doesn't interfere with the prompt, we run the
455 # prompt in a different event loop.
456 # If we don't do this, people could spawn coroutine with a
457 # while/true inside which will freeze the prompt.
458
459 old_loop = asyncio.get_event_loop()
460 asyncio.set_event_loop(self.pt_loop)
461 try:
462 text = self.pt_app.prompt(
463 default=default,
464 **self._extra_prompt_options())
465 finally:
466 # Restore the original event loop.
467 asyncio.set_event_loop(old_loop)
455 468 return text
456 469
457 470 def enable_win_unicode_console(self):
458 471 # Since IPython 7.10 doesn't support python < 3.6 and PEP 528, Python uses the unicode APIs for the Windows
459 472 # console by default, so WUC shouldn't be needed.
460 473 from warnings import warn
461 474 warn("`enable_win_unicode_console` is deprecated since IPython 7.10, does not do anything and will be removed in the future",
462 475 DeprecationWarning,
463 476 stacklevel=2)
464 477
465 478 def init_io(self):
466 479 if sys.platform not in {'win32', 'cli'}:
467 480 return
468 481
469 482 import colorama
470 483 colorama.init()
471 484
472 485 # For some reason we make these wrappers around stdout/stderr.
473 486 # For now, we need to reset them so all output gets coloured.
474 487 # https://github.com/ipython/ipython/issues/8669
475 488 # io.std* are deprecated, but don't show our own deprecation warnings
476 489 # during initialization of the deprecated API.
477 490 with warnings.catch_warnings():
478 491 warnings.simplefilter('ignore', DeprecationWarning)
479 492 io.stdout = io.IOStream(sys.stdout)
480 493 io.stderr = io.IOStream(sys.stderr)
481 494
482 495 def init_magics(self):
483 496 super(TerminalInteractiveShell, self).init_magics()
484 497 self.register_magics(TerminalMagics)
485 498
486 499 def init_alias(self):
487 500 # The parent class defines aliases that can be safely used with any
488 501 # frontend.
489 502 super(TerminalInteractiveShell, self).init_alias()
490 503
491 504 # Now define aliases that only make sense on the terminal, because they
492 505 # need direct access to the console in a way that we can't emulate in
493 506 # GUI or web frontend
494 507 if os.name == 'posix':
495 508 for cmd in ('clear', 'more', 'less', 'man'):
496 509 self.alias_manager.soft_define_alias(cmd, cmd)
497 510
498 511
499 512 def __init__(self, *args, **kwargs):
500 513 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
501 514 self.init_prompt_toolkit_cli()
502 515 self.init_term_title()
503 516 self.keep_running = True
504 517
505 518 self.debugger_history = InMemoryHistory()
506 519
507 520 def ask_exit(self):
508 521 self.keep_running = False
509 522
510 523 rl_next_input = None
511 524
512 525 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
513 526
514 527 if display_banner is not DISPLAY_BANNER_DEPRECATED:
515 528 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
516 529
517 530 self.keep_running = True
518 531 while self.keep_running:
519 532 print(self.separate_in, end='')
520 533
521 534 try:
522 535 code = self.prompt_for_code()
523 536 except EOFError:
524 537 if (not self.confirm_exit) \
525 538 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
526 539 self.ask_exit()
527 540
528 541 else:
529 542 if code:
530 543 self.run_cell(code, store_history=True)
531 544
532 545 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
533 546 # An extra layer of protection in case someone mashing Ctrl-C breaks
534 547 # out of our internal code.
535 548 if display_banner is not DISPLAY_BANNER_DEPRECATED:
536 549 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
537 550 while True:
538 551 try:
539 552 self.interact()
540 553 break
541 554 except KeyboardInterrupt as e:
542 555 print("\n%s escaped interact()\n" % type(e).__name__)
543 556 finally:
544 557 # An interrupt during the eventloop will mess up the
545 558 # internal state of the prompt_toolkit library.
546 559 # Stopping the eventloop fixes this, see
547 560 # https://github.com/ipython/ipython/pull/9867
548 561 if hasattr(self, '_eventloop'):
549 562 self._eventloop.stop()
550 563
551 564 self.restore_term_title()
552 565
553 566
554 567 _inputhook = None
555 568 def inputhook(self, context):
556 569 if self._inputhook is not None:
557 570 self._inputhook(context)
558 571
559 572 active_eventloop = None
560 573 def enable_gui(self, gui=None):
561 574 if gui and (gui != 'inline') :
562 575 self.active_eventloop, self._inputhook =\
563 576 get_inputhook_name_and_func(gui)
564 577 else:
565 578 self.active_eventloop = self._inputhook = None
566 579
567 580 # For prompt_toolkit 3.0. We have to create an asyncio event loop with
568 581 # this inputhook.
569 582 if PTK3:
570 583 if self._inputhook:
571 from prompt_toolkit.eventloop import set_eventloop_with_inputhook
572 set_eventloop_with_inputhook(self._inputhook)
584 from prompt_toolkit.eventloop import new_eventloop_with_inputhook
585 self.pt_loop = new_eventloop_with_inputhook(self._inputhook)
573 586 else:
574 587 import asyncio
575 asyncio.set_event_loop(asyncio.new_event_loop())
588 self.pt_loop = asyncio.new_event_loop()
576 589
577 590 # Run !system commands directly, not through pipes, so terminal programs
578 591 # work correctly.
579 592 system = InteractiveShell.system_raw
580 593
581 594 def auto_rewrite_input(self, cmd):
582 595 """Overridden from the parent class to use fancy rewriting prompt"""
583 596 if not self.show_rewritten_input:
584 597 return
585 598
586 599 tokens = self.prompts.rewrite_prompt_tokens()
587 600 if self.pt_app:
588 601 print_formatted_text(PygmentsTokens(tokens), end='',
589 602 style=self.pt_app.app.style)
590 603 print(cmd)
591 604 else:
592 605 prompt = ''.join(s for t, s in tokens)
593 606 print(prompt, cmd, sep='')
594 607
595 608 _prompts_before = None
596 609 def switch_doctest_mode(self, mode):
597 610 """Switch prompts to classic for %doctest_mode"""
598 611 if mode:
599 612 self._prompts_before = self.prompts
600 613 self.prompts = ClassicPrompts(self)
601 614 elif self._prompts_before:
602 615 self.prompts = self._prompts_before
603 616 self._prompts_before = None
604 617 # self._update_layout()
605 618
606 619
607 620 InteractiveShellABC.register(TerminalInteractiveShell)
608 621
609 622 if __name__ == '__main__':
610 623 TerminalInteractiveShell.instance().interact()
@@ -1,262 +1,262
1 1 #!/usr/bin/env python3
2 2 # -*- coding: utf-8 -*-
3 3 """Setup script for IPython.
4 4
5 5 Under Posix environments it works like a typical setup.py script.
6 6 Under Windows, the command sdist is not supported, since IPython
7 7 requires utilities which are not available under Windows."""
8 8
9 9 #-----------------------------------------------------------------------------
10 10 # Copyright (c) 2008-2011, IPython Development Team.
11 11 # Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu>
12 12 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
13 13 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
14 14 #
15 15 # Distributed under the terms of the Modified BSD License.
16 16 #
17 17 # The full license is in the file COPYING.rst, distributed with this software.
18 18 #-----------------------------------------------------------------------------
19 19
20 20 from __future__ import print_function
21 21
22 22 import os
23 23 import sys
24 24
25 25 # **Python version check**
26 26 #
27 27 # This check is also made in IPython/__init__, don't forget to update both when
28 28 # changing Python version requirements.
29 29 if sys.version_info < (3, 6):
30 30 pip_message = 'This may be due to an out of date pip. Make sure you have pip >= 9.0.1.'
31 31 try:
32 32 import pip
33 33 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
34 34 if pip_version < (9, 0, 1) :
35 35 pip_message = 'Your pip version is out of date, please install pip >= 9.0.1. '\
36 36 'pip {} detected.'.format(pip.__version__)
37 37 else:
38 38 # pip is new enough - it must be something else
39 39 pip_message = ''
40 40 except Exception:
41 41 pass
42 42
43 43
44 44 error = """
45 45 IPython 7.10+ supports Python 3.6 and above, following NEP 29.
46 46 When using Python 2.7, please install IPython 5.x LTS Long Term Support version.
47 47 Python 3.3 and 3.4 were supported up to IPython 6.x.
48 48 Python 3.5 was supported with IPython 7.0 to 7.9.
49 49
50 50 See IPython `README.rst` file for more information:
51 51
52 52 https://github.com/ipython/ipython/blob/master/README.rst
53 53
54 54 Python {py} detected.
55 55 {pip}
56 56 """.format(py=sys.version_info, pip=pip_message )
57 57
58 58 print(error, file=sys.stderr)
59 59 sys.exit(1)
60 60
61 61 # At least we're on the python version we need, move on.
62 62
63 63 # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
64 64 # update it when the contents of directories change.
65 65 if os.path.exists('MANIFEST'): os.remove('MANIFEST')
66 66
67 67 from distutils.core import setup
68 68
69 69 # Our own imports
70 70 from setupbase import target_update
71 71
72 72 from setupbase import (
73 73 setup_args,
74 74 find_packages,
75 75 find_package_data,
76 76 check_package_data_first,
77 77 find_entry_points,
78 78 build_scripts_entrypt,
79 79 find_data_files,
80 80 git_prebuild,
81 81 install_symlinked,
82 82 install_lib_symlink,
83 83 install_scripts_for_symlink,
84 84 unsymlink,
85 85 )
86 86
87 87 isfile = os.path.isfile
88 88 pjoin = os.path.join
89 89
90 90 #-------------------------------------------------------------------------------
91 91 # Handle OS specific things
92 92 #-------------------------------------------------------------------------------
93 93
94 94 if os.name in ('nt','dos'):
95 95 os_name = 'windows'
96 96 else:
97 97 os_name = os.name
98 98
99 99 # Under Windows, 'sdist' has not been supported. Now that the docs build with
100 100 # Sphinx it might work, but let's not turn it on until someone confirms that it
101 101 # actually works.
102 102 if os_name == 'windows' and 'sdist' in sys.argv:
103 103 print('The sdist command is not available under Windows. Exiting.')
104 104 sys.exit(1)
105 105
106 106
107 107 #-------------------------------------------------------------------------------
108 108 # Things related to the IPython documentation
109 109 #-------------------------------------------------------------------------------
110 110
111 111 # update the manuals when building a source dist
112 112 if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):
113 113
114 114 # List of things to be updated. Each entry is a triplet of args for
115 115 # target_update()
116 116 to_update = [
117 117 ('docs/man/ipython.1.gz',
118 118 ['docs/man/ipython.1'],
119 119 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'),
120 120 ]
121 121
122 122
123 123 [ target_update(*t) for t in to_update ]
124 124
125 125 #---------------------------------------------------------------------------
126 126 # Find all the packages, package data, and data_files
127 127 #---------------------------------------------------------------------------
128 128
129 129 packages = find_packages()
130 130 package_data = find_package_data()
131 131
132 132 data_files = find_data_files()
133 133
134 134 setup_args['packages'] = packages
135 135 setup_args['package_data'] = package_data
136 136 setup_args['data_files'] = data_files
137 137
138 138 #---------------------------------------------------------------------------
139 139 # custom distutils commands
140 140 #---------------------------------------------------------------------------
141 141 # imports here, so they are after setuptools import if there was one
142 142 from distutils.command.sdist import sdist
143 143
144 144 setup_args['cmdclass'] = {
145 145 'build_py': \
146 146 check_package_data_first(git_prebuild('IPython')),
147 147 'sdist' : git_prebuild('IPython', sdist),
148 148 'symlink': install_symlinked,
149 149 'install_lib_symlink': install_lib_symlink,
150 150 'install_scripts_sym': install_scripts_for_symlink,
151 151 'unsymlink': unsymlink,
152 152 }
153 153
154 154
155 155 #---------------------------------------------------------------------------
156 156 # Handle scripts, dependencies, and setuptools specific things
157 157 #---------------------------------------------------------------------------
158 158
159 159 # For some commands, use setuptools. Note that we do NOT list install here!
160 160 # If you want a setuptools-enhanced install, just run 'setupegg.py install'
161 161 needs_setuptools = {'develop', 'release', 'bdist_egg', 'bdist_rpm',
162 162 'bdist', 'bdist_dumb', 'bdist_wininst', 'bdist_wheel',
163 163 'egg_info', 'easy_install', 'upload', 'install_egg_info',
164 164 }
165 165
166 166 if len(needs_setuptools.intersection(sys.argv)) > 0:
167 167 import setuptools
168 168
169 169 # This dict is used for passing extra arguments that are setuptools
170 170 # specific to setup
171 171 setuptools_extra_args = {}
172 172
173 173 # setuptools requirements
174 174
175 175 extras_require = dict(
176 176 parallel = ['ipyparallel'],
177 177 qtconsole = ['qtconsole'],
178 178 doc = ['Sphinx>=1.3'],
179 179 test = ['nose>=0.10.1', 'requests', 'testpath', 'pygments', 'nbformat', 'ipykernel', 'numpy>=1.14'],
180 180 terminal = [],
181 181 kernel = ['ipykernel'],
182 182 nbformat = ['nbformat'],
183 183 notebook = ['notebook', 'ipywidgets'],
184 184 nbconvert = ['nbconvert'],
185 185 )
186 186
187 187 install_requires = [
188 188 'setuptools>=18.5',
189 189 'jedi>=0.10',
190 190 'decorator',
191 191 'pickleshare',
192 192 'traitlets>=4.2',
193 'prompt_toolkit>=2.0.0,<3.1.0',
193 'prompt_toolkit>=2.0.0,<3.1.0,!=3.0.0,!=3.0.1',
194 194 'pygments',
195 195 'backcall',
196 196 ]
197 197
198 198 # Platform-specific dependencies:
199 199 # This is the correct way to specify these,
200 200 # but requires pip >= 6. pip < 6 ignores these.
201 201
202 202 extras_require.update({
203 203 ':sys_platform != "win32"': ['pexpect'],
204 204 ':sys_platform == "darwin"': ['appnope'],
205 205 ':sys_platform == "win32"': ['colorama'],
206 206 })
207 207 # FIXME: re-specify above platform dependencies for pip < 6
208 208 # These would result in non-portable bdists.
209 209 if not any(arg.startswith('bdist') for arg in sys.argv):
210 210 if sys.platform == 'darwin':
211 211 install_requires.extend(['appnope'])
212 212
213 213 if not sys.platform.startswith('win'):
214 214 install_requires.append('pexpect')
215 215
216 216 # workaround pypa/setuptools#147, where setuptools misspells
217 217 # platform_python_implementation as python_implementation
218 218 if 'setuptools' in sys.modules:
219 219 for key in list(extras_require):
220 220 if 'platform_python_implementation' in key:
221 221 new_key = key.replace('platform_python_implementation', 'python_implementation')
222 222 extras_require[new_key] = extras_require.pop(key)
223 223
224 224 everything = set()
225 225 for key, deps in extras_require.items():
226 226 if ':' not in key:
227 227 everything.update(deps)
228 228 extras_require['all'] = everything
229 229
230 230 if 'setuptools' in sys.modules:
231 231 setuptools_extra_args['python_requires'] = '>=3.6'
232 232 setuptools_extra_args['zip_safe'] = False
233 233 setuptools_extra_args['entry_points'] = {
234 234 'console_scripts': find_entry_points(),
235 235 'pygments.lexers': [
236 236 'ipythonconsole = IPython.lib.lexers:IPythonConsoleLexer',
237 237 'ipython = IPython.lib.lexers:IPythonLexer',
238 238 'ipython3 = IPython.lib.lexers:IPython3Lexer',
239 239 ],
240 240 }
241 241 setup_args['extras_require'] = extras_require
242 242 setup_args['install_requires'] = install_requires
243 243
244 244 else:
245 245 # scripts has to be a non-empty list, or install_scripts isn't called
246 246 setup_args['scripts'] = [e.split('=')[0].strip() for e in find_entry_points()]
247 247
248 248 setup_args['cmdclass']['build_scripts'] = build_scripts_entrypt
249 249
250 250 #---------------------------------------------------------------------------
251 251 # Do the actual setup now
252 252 #---------------------------------------------------------------------------
253 253
254 254 setup_args.update(setuptools_extra_args)
255 255
256 256
257 257
258 258 def main():
259 259 setup(**setup_args)
260 260
261 261 if __name__ == '__main__':
262 262 main()
General Comments 0
You need to be logged in to leave comments. Login now