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