##// END OF EJS Templates
Merge pull request #9738 from takluyver/i9728...
Thomas Kluyver -
r22700:58a2cf45 merge
parent child Browse files
Show More
@@ -1,462 +1,452 b''
1 """IPython terminal interface using prompt_toolkit"""
1 """IPython terminal interface using prompt_toolkit"""
2 from __future__ import print_function
2 from __future__ import print_function
3
3
4 import os
4 import os
5 import sys
5 import sys
6 from warnings import warn
6 from warnings import warn
7
7
8 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
8 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
9 from IPython.utils.py3compat import PY3, cast_unicode_py2, input
9 from IPython.utils.py3compat import PY3, cast_unicode_py2, input
10 from IPython.utils.terminal import toggle_set_term_title, set_term_title
10 from IPython.utils.terminal import toggle_set_term_title, set_term_title
11 from IPython.utils.process import abbrev_cwd
11 from IPython.utils.process import abbrev_cwd
12 from traitlets import Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum
12 from traitlets import Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum
13
13
14 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
14 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
15 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
15 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
16 from prompt_toolkit.history import InMemoryHistory
16 from prompt_toolkit.history import InMemoryHistory
17 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout, create_output
17 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout, create_output
18 from prompt_toolkit.interface import CommandLineInterface
18 from prompt_toolkit.interface import CommandLineInterface
19 from prompt_toolkit.key_binding.manager import KeyBindingManager
19 from prompt_toolkit.key_binding.manager import KeyBindingManager
20 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
20 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
21 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
21 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
22
22
23 from pygments.styles import get_style_by_name, get_all_styles
23 from pygments.styles import get_style_by_name, get_all_styles
24 from pygments.token import Token
24 from pygments.token import Token
25
25
26 from .debugger import TerminalPdb, Pdb
26 from .debugger import TerminalPdb, Pdb
27 from .magics import TerminalMagics
27 from .magics import TerminalMagics
28 from .pt_inputhooks import get_inputhook_func
28 from .pt_inputhooks import get_inputhook_func
29 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
29 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
30 from .ptutils import IPythonPTCompleter, IPythonPTLexer
30 from .ptutils import IPythonPTCompleter, IPythonPTLexer
31 from .shortcuts import register_ipython_shortcuts
31 from .shortcuts import register_ipython_shortcuts
32
32
33 DISPLAY_BANNER_DEPRECATED = object()
33 DISPLAY_BANNER_DEPRECATED = object()
34
34
35
35
36 from pygments.style import Style
36 from pygments.style import Style
37
37
38 class _NoStyle(Style): pass
38 class _NoStyle(Style): pass
39
39
40
40
41
41
42 _style_overrides_light_bg = {
42 _style_overrides_light_bg = {
43 Token.Prompt: '#0000ff',
43 Token.Prompt: '#0000ff',
44 Token.PromptNum: '#0000ee bold',
44 Token.PromptNum: '#0000ee bold',
45 Token.OutPrompt: '#cc0000',
45 Token.OutPrompt: '#cc0000',
46 Token.OutPromptNum: '#bb0000 bold',
46 Token.OutPromptNum: '#bb0000 bold',
47 }
47 }
48
48
49 _style_overrides_linux = {
49 _style_overrides_linux = {
50 Token.Prompt: '#00cc00',
50 Token.Prompt: '#00cc00',
51 Token.PromptNum: '#00bb00 bold',
51 Token.PromptNum: '#00bb00 bold',
52 Token.OutPrompt: '#cc0000',
52 Token.OutPrompt: '#cc0000',
53 Token.OutPromptNum: '#bb0000 bold',
53 Token.OutPromptNum: '#bb0000 bold',
54 }
54 }
55
55
56
56
57
57
58 def get_default_editor():
58 def get_default_editor():
59 try:
59 try:
60 ed = os.environ['EDITOR']
60 ed = os.environ['EDITOR']
61 if not PY3:
61 if not PY3:
62 ed = ed.decode()
62 ed = ed.decode()
63 return ed
63 return ed
64 except KeyError:
64 except KeyError:
65 pass
65 pass
66 except UnicodeError:
66 except UnicodeError:
67 warn("$EDITOR environment variable is not pure ASCII. Using platform "
67 warn("$EDITOR environment variable is not pure ASCII. Using platform "
68 "default editor.")
68 "default editor.")
69
69
70 if os.name == 'posix':
70 if os.name == 'posix':
71 return 'vi' # the only one guaranteed to be there!
71 return 'vi' # the only one guaranteed to be there!
72 else:
72 else:
73 return 'notepad' # same in Windows!
73 return 'notepad' # same in Windows!
74
74
75
75
76 if sys.stdin and sys.stdout and sys.stderr:
76 if sys.stdin and sys.stdout and sys.stderr:
77 _is_tty = (sys.stdin.isatty()) and (sys.stdout.isatty()) and (sys.stderr.isatty())
77 _is_tty = (sys.stdin.isatty()) and (sys.stdout.isatty()) and (sys.stderr.isatty())
78 else:
78 else:
79 _is_tty = False
79 _is_tty = False
80
80
81
81
82 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
82 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
83
83
84 class TerminalInteractiveShell(InteractiveShell):
84 class TerminalInteractiveShell(InteractiveShell):
85 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
85 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
86 'to reserve for the completion menu'
86 'to reserve for the completion menu'
87 ).tag(config=True)
87 ).tag(config=True)
88
88
89 def _space_for_menu_changed(self, old, new):
89 def _space_for_menu_changed(self, old, new):
90 self._update_layout()
90 self._update_layout()
91
91
92 pt_cli = None
92 pt_cli = None
93 debugger_history = None
93 debugger_history = None
94 _pt_app = None
94 _pt_app = None
95
95
96 simple_prompt = Bool(_use_simple_prompt,
96 simple_prompt = Bool(_use_simple_prompt,
97 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
97 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
98
98
99 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
99 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
100 IPython own testing machinery, and emacs inferior-shell integration through elpy.
100 IPython own testing machinery, and emacs inferior-shell integration through elpy.
101
101
102 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
102 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
103 environment variable is set, or the current terminal is not a tty.
103 environment variable is set, or the current terminal is not a tty.
104
104
105 """
105 """
106 ).tag(config=True)
106 ).tag(config=True)
107
107
108 @property
108 @property
109 def debugger_cls(self):
109 def debugger_cls(self):
110 return Pdb if self.simple_prompt else TerminalPdb
110 return Pdb if self.simple_prompt else TerminalPdb
111
111
112 confirm_exit = Bool(True,
112 confirm_exit = Bool(True,
113 help="""
113 help="""
114 Set to confirm when you try to exit IPython with an EOF (Control-D
114 Set to confirm when you try to exit IPython with an EOF (Control-D
115 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
115 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
116 you can force a direct exit without any confirmation.""",
116 you can force a direct exit without any confirmation.""",
117 ).tag(config=True)
117 ).tag(config=True)
118
118
119 editing_mode = Unicode('emacs',
119 editing_mode = Unicode('emacs',
120 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
120 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
121 ).tag(config=True)
121 ).tag(config=True)
122
122
123 mouse_support = Bool(False,
123 mouse_support = Bool(False,
124 help="Enable mouse support in the prompt"
124 help="Enable mouse support in the prompt"
125 ).tag(config=True)
125 ).tag(config=True)
126
126
127 highlighting_style = Unicode('legacy',
127 highlighting_style = Unicode('legacy',
128 help="The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles())
128 help="The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles())
129 ).tag(config=True)
129 ).tag(config=True)
130
130
131
131
132 @observe('highlighting_style')
132 @observe('highlighting_style')
133 @observe('colors')
133 @observe('colors')
134 def _highlighting_style_changed(self, change):
134 def _highlighting_style_changed(self, change):
135 self.refresh_style()
135 self.refresh_style()
136
136
137 def refresh_style(self):
137 def refresh_style(self):
138 self._style = self._make_style_from_name(self.highlighting_style)
138 self._style = self._make_style_from_name(self.highlighting_style)
139
139
140
140
141 highlighting_style_overrides = Dict(
141 highlighting_style_overrides = Dict(
142 help="Override highlighting format for specific tokens"
142 help="Override highlighting format for specific tokens"
143 ).tag(config=True)
143 ).tag(config=True)
144
144
145 true_color = Bool(False,
145 true_color = Bool(False,
146 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
146 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
147 "If your terminal supports true color, the following command "
147 "If your terminal supports true color, the following command "
148 "should print 'TRUECOLOR' in orange: "
148 "should print 'TRUECOLOR' in orange: "
149 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
149 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
150 ).tag(config=True)
150 ).tag(config=True)
151
151
152 editor = Unicode(get_default_editor(),
152 editor = Unicode(get_default_editor(),
153 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
153 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
154 ).tag(config=True)
154 ).tag(config=True)
155
155
156 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
156 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
157
157
158 prompts = Instance(Prompts)
158 prompts = Instance(Prompts)
159
159
160 @default('prompts')
160 @default('prompts')
161 def _prompts_default(self):
161 def _prompts_default(self):
162 return self.prompts_class(self)
162 return self.prompts_class(self)
163
163
164 @observe('prompts')
164 @observe('prompts')
165 def _(self, change):
165 def _(self, change):
166 self._update_layout()
166 self._update_layout()
167
167
168 @default('displayhook_class')
168 @default('displayhook_class')
169 def _displayhook_class_default(self):
169 def _displayhook_class_default(self):
170 return RichPromptDisplayHook
170 return RichPromptDisplayHook
171
171
172 term_title = Bool(True,
172 term_title = Bool(True,
173 help="Automatically set the terminal title"
173 help="Automatically set the terminal title"
174 ).tag(config=True)
174 ).tag(config=True)
175
175
176 # Leaving that for beta/rc tester, shoudl remove for 5.0.0 final.
177 display_completions_in_columns = Bool(None,
178 help="DEPRECATED", allow_none=True
179 ).tag(config=True)
180
181 @observe('display_completions_in_columns')
182 def _display_completions_in_columns_changed(self, new):
183 raise DeprecationWarning("The `display_completions_in_columns` Boolean has been replaced by the enum `display_completions`"
184 "with the following acceptable value: 'column', 'multicolumn','readlinelike'. ")
185
186 display_completions = Enum(('column', 'multicolumn','readlinelike'), default_value='multicolumn').tag(config=True)
176 display_completions = Enum(('column', 'multicolumn','readlinelike'), default_value='multicolumn').tag(config=True)
187
177
188 highlight_matching_brackets = Bool(True,
178 highlight_matching_brackets = Bool(True,
189 help="Highlight matching brackets .",
179 help="Highlight matching brackets .",
190 ).tag(config=True)
180 ).tag(config=True)
191
181
192 @observe('term_title')
182 @observe('term_title')
193 def init_term_title(self, change=None):
183 def init_term_title(self, change=None):
194 # Enable or disable the terminal title.
184 # Enable or disable the terminal title.
195 if self.term_title:
185 if self.term_title:
196 toggle_set_term_title(True)
186 toggle_set_term_title(True)
197 set_term_title('IPython: ' + abbrev_cwd())
187 set_term_title('IPython: ' + abbrev_cwd())
198 else:
188 else:
199 toggle_set_term_title(False)
189 toggle_set_term_title(False)
200
190
201 def init_display_formatter(self):
191 def init_display_formatter(self):
202 super(TerminalInteractiveShell, self).init_display_formatter()
192 super(TerminalInteractiveShell, self).init_display_formatter()
203 # terminal only supports plain text
193 # terminal only supports plain text
204 self.display_formatter.active_types = ['text/plain']
194 self.display_formatter.active_types = ['text/plain']
205
195
206 def init_prompt_toolkit_cli(self):
196 def init_prompt_toolkit_cli(self):
207 if self.simple_prompt:
197 if self.simple_prompt:
208 # Fall back to plain non-interactive output for tests.
198 # Fall back to plain non-interactive output for tests.
209 # This is very limited, and only accepts a single line.
199 # This is very limited, and only accepts a single line.
210 def prompt():
200 def prompt():
211 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
201 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
212 self.prompt_for_code = prompt
202 self.prompt_for_code = prompt
213 return
203 return
214
204
215 # Set up keyboard shortcuts
205 # Set up keyboard shortcuts
216 kbmanager = KeyBindingManager.for_prompt()
206 kbmanager = KeyBindingManager.for_prompt()
217 register_ipython_shortcuts(kbmanager.registry, self)
207 register_ipython_shortcuts(kbmanager.registry, self)
218
208
219 # Pre-populate history from IPython's history database
209 # Pre-populate history from IPython's history database
220 history = InMemoryHistory()
210 history = InMemoryHistory()
221 last_cell = u""
211 last_cell = u""
222 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
212 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
223 include_latest=True):
213 include_latest=True):
224 # Ignore blank lines and consecutive duplicates
214 # Ignore blank lines and consecutive duplicates
225 cell = cell.rstrip()
215 cell = cell.rstrip()
226 if cell and (cell != last_cell):
216 if cell and (cell != last_cell):
227 history.append(cell)
217 history.append(cell)
228
218
229 self._style = self._make_style_from_name(self.highlighting_style)
219 self._style = self._make_style_from_name(self.highlighting_style)
230 style = DynamicStyle(lambda: self._style)
220 style = DynamicStyle(lambda: self._style)
231
221
232 editing_mode = getattr(EditingMode, self.editing_mode.upper())
222 editing_mode = getattr(EditingMode, self.editing_mode.upper())
233
223
234 self._pt_app = create_prompt_application(
224 self._pt_app = create_prompt_application(
235 editing_mode=editing_mode,
225 editing_mode=editing_mode,
236 key_bindings_registry=kbmanager.registry,
226 key_bindings_registry=kbmanager.registry,
237 history=history,
227 history=history,
238 completer=IPythonPTCompleter(self.Completer),
228 completer=IPythonPTCompleter(self.Completer),
239 enable_history_search=True,
229 enable_history_search=True,
240 style=style,
230 style=style,
241 mouse_support=self.mouse_support,
231 mouse_support=self.mouse_support,
242 **self._layout_options()
232 **self._layout_options()
243 )
233 )
244 self._eventloop = create_eventloop(self.inputhook)
234 self._eventloop = create_eventloop(self.inputhook)
245 self.pt_cli = CommandLineInterface(
235 self.pt_cli = CommandLineInterface(
246 self._pt_app, eventloop=self._eventloop,
236 self._pt_app, eventloop=self._eventloop,
247 output=create_output(true_color=self.true_color))
237 output=create_output(true_color=self.true_color))
248
238
249 def _make_style_from_name(self, name):
239 def _make_style_from_name(self, name):
250 """
240 """
251 Small wrapper that make an IPython compatible style from a style name
241 Small wrapper that make an IPython compatible style from a style name
252
242
253 We need that to add style for prompt ... etc.
243 We need that to add style for prompt ... etc.
254 """
244 """
255 style_overrides = {}
245 style_overrides = {}
256 if name == 'legacy':
246 if name == 'legacy':
257 legacy = self.colors.lower()
247 legacy = self.colors.lower()
258 if legacy == 'linux':
248 if legacy == 'linux':
259 style_cls = get_style_by_name('monokai')
249 style_cls = get_style_by_name('monokai')
260 style_overrides = _style_overrides_linux
250 style_overrides = _style_overrides_linux
261 elif legacy == 'lightbg':
251 elif legacy == 'lightbg':
262 style_overrides = _style_overrides_light_bg
252 style_overrides = _style_overrides_light_bg
263 style_cls = get_style_by_name('pastie')
253 style_cls = get_style_by_name('pastie')
264 elif legacy == 'neutral':
254 elif legacy == 'neutral':
265 # The default theme needs to be visible on both a dark background
255 # The default theme needs to be visible on both a dark background
266 # and a light background, because we can't tell what the terminal
256 # and a light background, because we can't tell what the terminal
267 # looks like. These tweaks to the default theme help with that.
257 # looks like. These tweaks to the default theme help with that.
268 style_cls = get_style_by_name('default')
258 style_cls = get_style_by_name('default')
269 style_overrides.update({
259 style_overrides.update({
270 Token.Number: '#007700',
260 Token.Number: '#007700',
271 Token.Operator: 'noinherit',
261 Token.Operator: 'noinherit',
272 Token.String: '#BB6622',
262 Token.String: '#BB6622',
273 Token.Name.Function: '#2080D0',
263 Token.Name.Function: '#2080D0',
274 Token.Name.Class: 'bold #2080D0',
264 Token.Name.Class: 'bold #2080D0',
275 Token.Name.Namespace: 'bold #2080D0',
265 Token.Name.Namespace: 'bold #2080D0',
276 Token.Prompt: '#009900',
266 Token.Prompt: '#009900',
277 Token.PromptNum: '#00ff00 bold',
267 Token.PromptNum: '#00ff00 bold',
278 Token.OutPrompt: '#990000',
268 Token.OutPrompt: '#990000',
279 Token.OutPromptNum: '#ff0000 bold',
269 Token.OutPromptNum: '#ff0000 bold',
280 })
270 })
281 elif legacy =='nocolor':
271 elif legacy =='nocolor':
282 style_cls=_NoStyle
272 style_cls=_NoStyle
283 style_overrides = {}
273 style_overrides = {}
284 else :
274 else :
285 raise ValueError('Got unknown colors: ', legacy)
275 raise ValueError('Got unknown colors: ', legacy)
286 else :
276 else :
287 style_cls = get_style_by_name(name)
277 style_cls = get_style_by_name(name)
288 style_overrides = {
278 style_overrides = {
289 Token.Prompt: '#009900',
279 Token.Prompt: '#009900',
290 Token.PromptNum: '#00ff00 bold',
280 Token.PromptNum: '#00ff00 bold',
291 Token.OutPrompt: '#990000',
281 Token.OutPrompt: '#990000',
292 Token.OutPromptNum: '#ff0000 bold',
282 Token.OutPromptNum: '#ff0000 bold',
293 }
283 }
294 style_overrides.update(self.highlighting_style_overrides)
284 style_overrides.update(self.highlighting_style_overrides)
295 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
285 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
296 style_dict=style_overrides)
286 style_dict=style_overrides)
297
287
298 return style
288 return style
299
289
300 def _layout_options(self):
290 def _layout_options(self):
301 """
291 """
302 Return the current layout option for the current Terminal InteractiveShell
292 Return the current layout option for the current Terminal InteractiveShell
303 """
293 """
304 return {
294 return {
305 'lexer':IPythonPTLexer(),
295 'lexer':IPythonPTLexer(),
306 'reserve_space_for_menu':self.space_for_menu,
296 'reserve_space_for_menu':self.space_for_menu,
307 'get_prompt_tokens':self.prompts.in_prompt_tokens,
297 'get_prompt_tokens':self.prompts.in_prompt_tokens,
308 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
298 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
309 'multiline':True,
299 'multiline':True,
310 'display_completions_in_columns': (self.display_completions == 'multicolumn'),
300 'display_completions_in_columns': (self.display_completions == 'multicolumn'),
311
301
312 # Highlight matching brackets, but only when this setting is
302 # Highlight matching brackets, but only when this setting is
313 # enabled, and only when the DEFAULT_BUFFER has the focus.
303 # enabled, and only when the DEFAULT_BUFFER has the focus.
314 'extra_input_processors': [ConditionalProcessor(
304 'extra_input_processors': [ConditionalProcessor(
315 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
305 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
316 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
306 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
317 Condition(lambda cli: self.highlight_matching_brackets))],
307 Condition(lambda cli: self.highlight_matching_brackets))],
318 }
308 }
319
309
320 def _update_layout(self):
310 def _update_layout(self):
321 """
311 """
322 Ask for a re computation of the application layout, if for example ,
312 Ask for a re computation of the application layout, if for example ,
323 some configuration options have changed.
313 some configuration options have changed.
324 """
314 """
325 if self._pt_app:
315 if self._pt_app:
326 self._pt_app.layout = create_prompt_layout(**self._layout_options())
316 self._pt_app.layout = create_prompt_layout(**self._layout_options())
327
317
328 def prompt_for_code(self):
318 def prompt_for_code(self):
329 document = self.pt_cli.run(
319 document = self.pt_cli.run(
330 pre_run=self.pre_prompt, reset_current_buffer=True)
320 pre_run=self.pre_prompt, reset_current_buffer=True)
331 return document.text
321 return document.text
332
322
333 def init_io(self):
323 def init_io(self):
334 if sys.platform not in {'win32', 'cli'}:
324 if sys.platform not in {'win32', 'cli'}:
335 return
325 return
336
326
337 import win_unicode_console
327 import win_unicode_console
338 import colorama
328 import colorama
339
329
340 win_unicode_console.enable()
330 win_unicode_console.enable()
341 colorama.init()
331 colorama.init()
342
332
343 # For some reason we make these wrappers around stdout/stderr.
333 # For some reason we make these wrappers around stdout/stderr.
344 # For now, we need to reset them so all output gets coloured.
334 # For now, we need to reset them so all output gets coloured.
345 # https://github.com/ipython/ipython/issues/8669
335 # https://github.com/ipython/ipython/issues/8669
346 from IPython.utils import io
336 from IPython.utils import io
347 io.stdout = io.IOStream(sys.stdout)
337 io.stdout = io.IOStream(sys.stdout)
348 io.stderr = io.IOStream(sys.stderr)
338 io.stderr = io.IOStream(sys.stderr)
349
339
350 def init_magics(self):
340 def init_magics(self):
351 super(TerminalInteractiveShell, self).init_magics()
341 super(TerminalInteractiveShell, self).init_magics()
352 self.register_magics(TerminalMagics)
342 self.register_magics(TerminalMagics)
353
343
354 def init_alias(self):
344 def init_alias(self):
355 # The parent class defines aliases that can be safely used with any
345 # The parent class defines aliases that can be safely used with any
356 # frontend.
346 # frontend.
357 super(TerminalInteractiveShell, self).init_alias()
347 super(TerminalInteractiveShell, self).init_alias()
358
348
359 # Now define aliases that only make sense on the terminal, because they
349 # Now define aliases that only make sense on the terminal, because they
360 # need direct access to the console in a way that we can't emulate in
350 # need direct access to the console in a way that we can't emulate in
361 # GUI or web frontend
351 # GUI or web frontend
362 if os.name == 'posix':
352 if os.name == 'posix':
363 for cmd in ['clear', 'more', 'less', 'man']:
353 for cmd in ['clear', 'more', 'less', 'man']:
364 self.alias_manager.soft_define_alias(cmd, cmd)
354 self.alias_manager.soft_define_alias(cmd, cmd)
365
355
366
356
367 def __init__(self, *args, **kwargs):
357 def __init__(self, *args, **kwargs):
368 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
358 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
369 self.init_prompt_toolkit_cli()
359 self.init_prompt_toolkit_cli()
370 self.init_term_title()
360 self.init_term_title()
371 self.keep_running = True
361 self.keep_running = True
372
362
373 self.debugger_history = InMemoryHistory()
363 self.debugger_history = InMemoryHistory()
374
364
375 def ask_exit(self):
365 def ask_exit(self):
376 self.keep_running = False
366 self.keep_running = False
377
367
378 rl_next_input = None
368 rl_next_input = None
379
369
380 def pre_prompt(self):
370 def pre_prompt(self):
381 if self.rl_next_input:
371 if self.rl_next_input:
382 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
372 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
383 self.rl_next_input = None
373 self.rl_next_input = None
384
374
385 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
375 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
386
376
387 if display_banner is not DISPLAY_BANNER_DEPRECATED:
377 if display_banner is not DISPLAY_BANNER_DEPRECATED:
388 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
378 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
389
379
390 while self.keep_running:
380 while self.keep_running:
391 print(self.separate_in, end='')
381 print(self.separate_in, end='')
392
382
393 try:
383 try:
394 code = self.prompt_for_code()
384 code = self.prompt_for_code()
395 except EOFError:
385 except EOFError:
396 if (not self.confirm_exit) \
386 if (not self.confirm_exit) \
397 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
387 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
398 self.ask_exit()
388 self.ask_exit()
399
389
400 else:
390 else:
401 if code:
391 if code:
402 self.run_cell(code, store_history=True)
392 self.run_cell(code, store_history=True)
403
393
404 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
394 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
405 # An extra layer of protection in case someone mashing Ctrl-C breaks
395 # An extra layer of protection in case someone mashing Ctrl-C breaks
406 # out of our internal code.
396 # out of our internal code.
407 if display_banner is not DISPLAY_BANNER_DEPRECATED:
397 if display_banner is not DISPLAY_BANNER_DEPRECATED:
408 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
398 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
409 while True:
399 while True:
410 try:
400 try:
411 self.interact()
401 self.interact()
412 break
402 break
413 except KeyboardInterrupt:
403 except KeyboardInterrupt:
414 print("\nKeyboardInterrupt escaped interact()\n")
404 print("\nKeyboardInterrupt escaped interact()\n")
415
405
416 if hasattr(self, '_eventloop'):
406 if hasattr(self, '_eventloop'):
417 self._eventloop.close()
407 self._eventloop.close()
418
408
419 _inputhook = None
409 _inputhook = None
420 def inputhook(self, context):
410 def inputhook(self, context):
421 if self._inputhook is not None:
411 if self._inputhook is not None:
422 self._inputhook(context)
412 self._inputhook(context)
423
413
424 def enable_gui(self, gui=None):
414 def enable_gui(self, gui=None):
425 if gui:
415 if gui:
426 self._inputhook = get_inputhook_func(gui)
416 self._inputhook = get_inputhook_func(gui)
427 else:
417 else:
428 self._inputhook = None
418 self._inputhook = None
429
419
430 # Run !system commands directly, not through pipes, so terminal programs
420 # Run !system commands directly, not through pipes, so terminal programs
431 # work correctly.
421 # work correctly.
432 system = InteractiveShell.system_raw
422 system = InteractiveShell.system_raw
433
423
434 def auto_rewrite_input(self, cmd):
424 def auto_rewrite_input(self, cmd):
435 """Overridden from the parent class to use fancy rewriting prompt"""
425 """Overridden from the parent class to use fancy rewriting prompt"""
436 if not self.show_rewritten_input:
426 if not self.show_rewritten_input:
437 return
427 return
438
428
439 tokens = self.prompts.rewrite_prompt_tokens()
429 tokens = self.prompts.rewrite_prompt_tokens()
440 if self.pt_cli:
430 if self.pt_cli:
441 self.pt_cli.print_tokens(tokens)
431 self.pt_cli.print_tokens(tokens)
442 print(cmd)
432 print(cmd)
443 else:
433 else:
444 prompt = ''.join(s for t, s in tokens)
434 prompt = ''.join(s for t, s in tokens)
445 print(prompt, cmd, sep='')
435 print(prompt, cmd, sep='')
446
436
447 _prompts_before = None
437 _prompts_before = None
448 def switch_doctest_mode(self, mode):
438 def switch_doctest_mode(self, mode):
449 """Switch prompts to classic for %doctest_mode"""
439 """Switch prompts to classic for %doctest_mode"""
450 if mode:
440 if mode:
451 self._prompts_before = self.prompts
441 self._prompts_before = self.prompts
452 self.prompts = ClassicPrompts(self)
442 self.prompts = ClassicPrompts(self)
453 elif self._prompts_before:
443 elif self._prompts_before:
454 self.prompts = self._prompts_before
444 self.prompts = self._prompts_before
455 self._prompts_before = None
445 self._prompts_before = None
456 self._update_layout()
446 self._update_layout()
457
447
458
448
459 InteractiveShellABC.register(TerminalInteractiveShell)
449 InteractiveShellABC.register(TerminalInteractiveShell)
460
450
461 if __name__ == '__main__':
451 if __name__ == '__main__':
462 TerminalInteractiveShell.instance().interact()
452 TerminalInteractiveShell.instance().interact()
General Comments 0
You need to be logged in to leave comments. Login now