##// END OF EJS Templates
rename reset parameter to 'clear' for clarity
Paul Ivanov -
Show More
@@ -1,758 +1,758 b''
1 from __future__ import print_function
1 from __future__ import print_function
2
2
3 # Standard library imports
3 # Standard library imports
4 from collections import namedtuple
4 from collections import namedtuple
5 import sys
5 import sys
6 import time
6 import time
7 import uuid
7 import uuid
8
8
9 # System library imports
9 # System library imports
10 from pygments.lexers import PythonLexer
10 from pygments.lexers import PythonLexer
11 from IPython.external import qt
11 from IPython.external import qt
12 from IPython.external.qt import QtCore, QtGui
12 from IPython.external.qt import QtCore, QtGui
13
13
14 # Local imports
14 # Local imports
15 from IPython.core.inputsplitter import InputSplitter, transform_classic_prompt
15 from IPython.core.inputsplitter import InputSplitter, transform_classic_prompt
16 from IPython.core.oinspect import call_tip
16 from IPython.core.oinspect import call_tip
17 from IPython.frontend.qt.base_frontend_mixin import BaseFrontendMixin
17 from IPython.frontend.qt.base_frontend_mixin import BaseFrontendMixin
18 from IPython.utils.traitlets import Bool, Instance, Unicode
18 from IPython.utils.traitlets import Bool, Instance, Unicode
19 from bracket_matcher import BracketMatcher
19 from bracket_matcher import BracketMatcher
20 from call_tip_widget import CallTipWidget
20 from call_tip_widget import CallTipWidget
21 from completion_lexer import CompletionLexer
21 from completion_lexer import CompletionLexer
22 from history_console_widget import HistoryConsoleWidget
22 from history_console_widget import HistoryConsoleWidget
23 from pygments_highlighter import PygmentsHighlighter
23 from pygments_highlighter import PygmentsHighlighter
24
24
25
25
26 class FrontendHighlighter(PygmentsHighlighter):
26 class FrontendHighlighter(PygmentsHighlighter):
27 """ A PygmentsHighlighter that understands and ignores prompts.
27 """ A PygmentsHighlighter that understands and ignores prompts.
28 """
28 """
29
29
30 def __init__(self, frontend):
30 def __init__(self, frontend):
31 super(FrontendHighlighter, self).__init__(frontend._control.document())
31 super(FrontendHighlighter, self).__init__(frontend._control.document())
32 self._current_offset = 0
32 self._current_offset = 0
33 self._frontend = frontend
33 self._frontend = frontend
34 self.highlighting_on = False
34 self.highlighting_on = False
35
35
36 def highlightBlock(self, string):
36 def highlightBlock(self, string):
37 """ Highlight a block of text. Reimplemented to highlight selectively.
37 """ Highlight a block of text. Reimplemented to highlight selectively.
38 """
38 """
39 if not self.highlighting_on:
39 if not self.highlighting_on:
40 return
40 return
41
41
42 # The input to this function is a unicode string that may contain
42 # The input to this function is a unicode string that may contain
43 # paragraph break characters, non-breaking spaces, etc. Here we acquire
43 # paragraph break characters, non-breaking spaces, etc. Here we acquire
44 # the string as plain text so we can compare it.
44 # the string as plain text so we can compare it.
45 current_block = self.currentBlock()
45 current_block = self.currentBlock()
46 string = self._frontend._get_block_plain_text(current_block)
46 string = self._frontend._get_block_plain_text(current_block)
47
47
48 # Decide whether to check for the regular or continuation prompt.
48 # Decide whether to check for the regular or continuation prompt.
49 if current_block.contains(self._frontend._prompt_pos):
49 if current_block.contains(self._frontend._prompt_pos):
50 prompt = self._frontend._prompt
50 prompt = self._frontend._prompt
51 else:
51 else:
52 prompt = self._frontend._continuation_prompt
52 prompt = self._frontend._continuation_prompt
53
53
54 # Only highlight if we can identify a prompt, but make sure not to
54 # Only highlight if we can identify a prompt, but make sure not to
55 # highlight the prompt.
55 # highlight the prompt.
56 if string.startswith(prompt):
56 if string.startswith(prompt):
57 self._current_offset = len(prompt)
57 self._current_offset = len(prompt)
58 string = string[len(prompt):]
58 string = string[len(prompt):]
59 super(FrontendHighlighter, self).highlightBlock(string)
59 super(FrontendHighlighter, self).highlightBlock(string)
60
60
61 def rehighlightBlock(self, block):
61 def rehighlightBlock(self, block):
62 """ Reimplemented to temporarily enable highlighting if disabled.
62 """ Reimplemented to temporarily enable highlighting if disabled.
63 """
63 """
64 old = self.highlighting_on
64 old = self.highlighting_on
65 self.highlighting_on = True
65 self.highlighting_on = True
66 super(FrontendHighlighter, self).rehighlightBlock(block)
66 super(FrontendHighlighter, self).rehighlightBlock(block)
67 self.highlighting_on = old
67 self.highlighting_on = old
68
68
69 def setFormat(self, start, count, format):
69 def setFormat(self, start, count, format):
70 """ Reimplemented to highlight selectively.
70 """ Reimplemented to highlight selectively.
71 """
71 """
72 start += self._current_offset
72 start += self._current_offset
73 super(FrontendHighlighter, self).setFormat(start, count, format)
73 super(FrontendHighlighter, self).setFormat(start, count, format)
74
74
75
75
76 class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):
76 class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):
77 """ A Qt frontend for a generic Python kernel.
77 """ A Qt frontend for a generic Python kernel.
78 """
78 """
79
79
80 # The text to show when the kernel is (re)started.
80 # The text to show when the kernel is (re)started.
81 banner = Unicode()
81 banner = Unicode()
82
82
83 # An option and corresponding signal for overriding the default kernel
83 # An option and corresponding signal for overriding the default kernel
84 # interrupt behavior.
84 # interrupt behavior.
85 custom_interrupt = Bool(False)
85 custom_interrupt = Bool(False)
86 custom_interrupt_requested = QtCore.Signal()
86 custom_interrupt_requested = QtCore.Signal()
87
87
88 # An option and corresponding signals for overriding the default kernel
88 # An option and corresponding signals for overriding the default kernel
89 # restart behavior.
89 # restart behavior.
90 custom_restart = Bool(False)
90 custom_restart = Bool(False)
91 custom_restart_kernel_died = QtCore.Signal(float)
91 custom_restart_kernel_died = QtCore.Signal(float)
92 custom_restart_requested = QtCore.Signal()
92 custom_restart_requested = QtCore.Signal()
93
93
94 # Whether to automatically show calltips on open-parentheses.
94 # Whether to automatically show calltips on open-parentheses.
95 enable_calltips = Bool(True, config=True,
95 enable_calltips = Bool(True, config=True,
96 help="Whether to draw information calltips on open-parentheses.")
96 help="Whether to draw information calltips on open-parentheses.")
97
97
98 clear_on_kernel_restart = Bool(True, config=True,
98 clear_on_kernel_restart = Bool(True, config=True,
99 help="Whether to clear the console when the kernel is restarted")
99 help="Whether to clear the console when the kernel is restarted")
100
100
101 # Emitted when a user visible 'execute_request' has been submitted to the
101 # Emitted when a user visible 'execute_request' has been submitted to the
102 # kernel from the FrontendWidget. Contains the code to be executed.
102 # kernel from the FrontendWidget. Contains the code to be executed.
103 executing = QtCore.Signal(object)
103 executing = QtCore.Signal(object)
104
104
105 # Emitted when a user-visible 'execute_reply' has been received from the
105 # Emitted when a user-visible 'execute_reply' has been received from the
106 # kernel and processed by the FrontendWidget. Contains the response message.
106 # kernel and processed by the FrontendWidget. Contains the response message.
107 executed = QtCore.Signal(object)
107 executed = QtCore.Signal(object)
108
108
109 # Emitted when an exit request has been received from the kernel.
109 # Emitted when an exit request has been received from the kernel.
110 exit_requested = QtCore.Signal(object)
110 exit_requested = QtCore.Signal(object)
111
111
112 # Protected class variables.
112 # Protected class variables.
113 _transform_prompt = staticmethod(transform_classic_prompt)
113 _transform_prompt = staticmethod(transform_classic_prompt)
114 _CallTipRequest = namedtuple('_CallTipRequest', ['id', 'pos'])
114 _CallTipRequest = namedtuple('_CallTipRequest', ['id', 'pos'])
115 _CompletionRequest = namedtuple('_CompletionRequest', ['id', 'pos'])
115 _CompletionRequest = namedtuple('_CompletionRequest', ['id', 'pos'])
116 _ExecutionRequest = namedtuple('_ExecutionRequest', ['id', 'kind'])
116 _ExecutionRequest = namedtuple('_ExecutionRequest', ['id', 'kind'])
117 _input_splitter_class = InputSplitter
117 _input_splitter_class = InputSplitter
118 _local_kernel = False
118 _local_kernel = False
119 _highlighter = Instance(FrontendHighlighter)
119 _highlighter = Instance(FrontendHighlighter)
120
120
121 #---------------------------------------------------------------------------
121 #---------------------------------------------------------------------------
122 # 'object' interface
122 # 'object' interface
123 #---------------------------------------------------------------------------
123 #---------------------------------------------------------------------------
124
124
125 def __init__(self, *args, **kw):
125 def __init__(self, *args, **kw):
126 super(FrontendWidget, self).__init__(*args, **kw)
126 super(FrontendWidget, self).__init__(*args, **kw)
127 # FIXME: remove this when PySide min version is updated past 1.0.7
127 # FIXME: remove this when PySide min version is updated past 1.0.7
128 # forcefully disable calltips if PySide is < 1.0.7, because they crash
128 # forcefully disable calltips if PySide is < 1.0.7, because they crash
129 if qt.QT_API == qt.QT_API_PYSIDE:
129 if qt.QT_API == qt.QT_API_PYSIDE:
130 import PySide
130 import PySide
131 if PySide.__version_info__ < (1,0,7):
131 if PySide.__version_info__ < (1,0,7):
132 self.log.warn("PySide %s < 1.0.7 detected, disabling calltips" % PySide.__version__)
132 self.log.warn("PySide %s < 1.0.7 detected, disabling calltips" % PySide.__version__)
133 self.enable_calltips = False
133 self.enable_calltips = False
134
134
135 # FrontendWidget protected variables.
135 # FrontendWidget protected variables.
136 self._bracket_matcher = BracketMatcher(self._control)
136 self._bracket_matcher = BracketMatcher(self._control)
137 self._call_tip_widget = CallTipWidget(self._control)
137 self._call_tip_widget = CallTipWidget(self._control)
138 self._completion_lexer = CompletionLexer(PythonLexer())
138 self._completion_lexer = CompletionLexer(PythonLexer())
139 self._copy_raw_action = QtGui.QAction('Copy (Raw Text)', None)
139 self._copy_raw_action = QtGui.QAction('Copy (Raw Text)', None)
140 self._hidden = False
140 self._hidden = False
141 self._highlighter = FrontendHighlighter(self)
141 self._highlighter = FrontendHighlighter(self)
142 self._input_splitter = self._input_splitter_class(input_mode='cell')
142 self._input_splitter = self._input_splitter_class(input_mode='cell')
143 self._kernel_manager = None
143 self._kernel_manager = None
144 self._request_info = {}
144 self._request_info = {}
145 self._request_info['execute'] = {};
145 self._request_info['execute'] = {};
146 self._callback_dict = {}
146 self._callback_dict = {}
147
147
148 # Configure the ConsoleWidget.
148 # Configure the ConsoleWidget.
149 self.tab_width = 4
149 self.tab_width = 4
150 self._set_continuation_prompt('... ')
150 self._set_continuation_prompt('... ')
151
151
152 # Configure the CallTipWidget.
152 # Configure the CallTipWidget.
153 self._call_tip_widget.setFont(self.font)
153 self._call_tip_widget.setFont(self.font)
154 self.font_changed.connect(self._call_tip_widget.setFont)
154 self.font_changed.connect(self._call_tip_widget.setFont)
155
155
156 # Configure actions.
156 # Configure actions.
157 action = self._copy_raw_action
157 action = self._copy_raw_action
158 key = QtCore.Qt.CTRL | QtCore.Qt.SHIFT | QtCore.Qt.Key_C
158 key = QtCore.Qt.CTRL | QtCore.Qt.SHIFT | QtCore.Qt.Key_C
159 action.setEnabled(False)
159 action.setEnabled(False)
160 action.setShortcut(QtGui.QKeySequence(key))
160 action.setShortcut(QtGui.QKeySequence(key))
161 action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
161 action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
162 action.triggered.connect(self.copy_raw)
162 action.triggered.connect(self.copy_raw)
163 self.copy_available.connect(action.setEnabled)
163 self.copy_available.connect(action.setEnabled)
164 self.addAction(action)
164 self.addAction(action)
165
165
166 # Connect signal handlers.
166 # Connect signal handlers.
167 document = self._control.document()
167 document = self._control.document()
168 document.contentsChange.connect(self._document_contents_change)
168 document.contentsChange.connect(self._document_contents_change)
169
169
170 # Set flag for whether we are connected via localhost.
170 # Set flag for whether we are connected via localhost.
171 self._local_kernel = kw.get('local_kernel',
171 self._local_kernel = kw.get('local_kernel',
172 FrontendWidget._local_kernel)
172 FrontendWidget._local_kernel)
173
173
174 #---------------------------------------------------------------------------
174 #---------------------------------------------------------------------------
175 # 'ConsoleWidget' public interface
175 # 'ConsoleWidget' public interface
176 #---------------------------------------------------------------------------
176 #---------------------------------------------------------------------------
177
177
178 def copy(self):
178 def copy(self):
179 """ Copy the currently selected text to the clipboard, removing prompts.
179 """ Copy the currently selected text to the clipboard, removing prompts.
180 """
180 """
181 if self._page_control.hasFocus():
181 if self._page_control.hasFocus():
182 self._page_control.copy()
182 self._page_control.copy()
183 elif self._control.hasFocus():
183 elif self._control.hasFocus():
184 text = self._control.textCursor().selection().toPlainText()
184 text = self._control.textCursor().selection().toPlainText()
185 if text:
185 if text:
186 lines = map(self._transform_prompt, text.splitlines())
186 lines = map(self._transform_prompt, text.splitlines())
187 text = '\n'.join(lines)
187 text = '\n'.join(lines)
188 QtGui.QApplication.clipboard().setText(text)
188 QtGui.QApplication.clipboard().setText(text)
189 else:
189 else:
190 self.log.debug("frontend widget : unknown copy target")
190 self.log.debug("frontend widget : unknown copy target")
191
191
192 #---------------------------------------------------------------------------
192 #---------------------------------------------------------------------------
193 # 'ConsoleWidget' abstract interface
193 # 'ConsoleWidget' abstract interface
194 #---------------------------------------------------------------------------
194 #---------------------------------------------------------------------------
195
195
196 def _is_complete(self, source, interactive):
196 def _is_complete(self, source, interactive):
197 """ Returns whether 'source' can be completely processed and a new
197 """ Returns whether 'source' can be completely processed and a new
198 prompt created. When triggered by an Enter/Return key press,
198 prompt created. When triggered by an Enter/Return key press,
199 'interactive' is True; otherwise, it is False.
199 'interactive' is True; otherwise, it is False.
200 """
200 """
201 complete = self._input_splitter.push(source)
201 complete = self._input_splitter.push(source)
202 if interactive:
202 if interactive:
203 complete = not self._input_splitter.push_accepts_more()
203 complete = not self._input_splitter.push_accepts_more()
204 return complete
204 return complete
205
205
206 def _execute(self, source, hidden):
206 def _execute(self, source, hidden):
207 """ Execute 'source'. If 'hidden', do not show any output.
207 """ Execute 'source'. If 'hidden', do not show any output.
208
208
209 See parent class :meth:`execute` docstring for full details.
209 See parent class :meth:`execute` docstring for full details.
210 """
210 """
211 msg_id = self.kernel_manager.shell_channel.execute(source, hidden)
211 msg_id = self.kernel_manager.shell_channel.execute(source, hidden)
212 self._request_info['execute'][msg_id] = self._ExecutionRequest(msg_id, 'user')
212 self._request_info['execute'][msg_id] = self._ExecutionRequest(msg_id, 'user')
213 self._hidden = hidden
213 self._hidden = hidden
214 if not hidden:
214 if not hidden:
215 self.executing.emit(source)
215 self.executing.emit(source)
216
216
217 def _prompt_started_hook(self):
217 def _prompt_started_hook(self):
218 """ Called immediately after a new prompt is displayed.
218 """ Called immediately after a new prompt is displayed.
219 """
219 """
220 if not self._reading:
220 if not self._reading:
221 self._highlighter.highlighting_on = True
221 self._highlighter.highlighting_on = True
222
222
223 def _prompt_finished_hook(self):
223 def _prompt_finished_hook(self):
224 """ Called immediately after a prompt is finished, i.e. when some input
224 """ Called immediately after a prompt is finished, i.e. when some input
225 will be processed and a new prompt displayed.
225 will be processed and a new prompt displayed.
226 """
226 """
227 # Flush all state from the input splitter so the next round of
227 # Flush all state from the input splitter so the next round of
228 # reading input starts with a clean buffer.
228 # reading input starts with a clean buffer.
229 self._input_splitter.reset()
229 self._input_splitter.reset()
230
230
231 if not self._reading:
231 if not self._reading:
232 self._highlighter.highlighting_on = False
232 self._highlighter.highlighting_on = False
233
233
234 def _tab_pressed(self):
234 def _tab_pressed(self):
235 """ Called when the tab key is pressed. Returns whether to continue
235 """ Called when the tab key is pressed. Returns whether to continue
236 processing the event.
236 processing the event.
237 """
237 """
238 # Perform tab completion if:
238 # Perform tab completion if:
239 # 1) The cursor is in the input buffer.
239 # 1) The cursor is in the input buffer.
240 # 2) There is a non-whitespace character before the cursor.
240 # 2) There is a non-whitespace character before the cursor.
241 text = self._get_input_buffer_cursor_line()
241 text = self._get_input_buffer_cursor_line()
242 if text is None:
242 if text is None:
243 return False
243 return False
244 complete = bool(text[:self._get_input_buffer_cursor_column()].strip())
244 complete = bool(text[:self._get_input_buffer_cursor_column()].strip())
245 if complete:
245 if complete:
246 self._complete()
246 self._complete()
247 return not complete
247 return not complete
248
248
249 #---------------------------------------------------------------------------
249 #---------------------------------------------------------------------------
250 # 'ConsoleWidget' protected interface
250 # 'ConsoleWidget' protected interface
251 #---------------------------------------------------------------------------
251 #---------------------------------------------------------------------------
252
252
253 def _context_menu_make(self, pos):
253 def _context_menu_make(self, pos):
254 """ Reimplemented to add an action for raw copy.
254 """ Reimplemented to add an action for raw copy.
255 """
255 """
256 menu = super(FrontendWidget, self)._context_menu_make(pos)
256 menu = super(FrontendWidget, self)._context_menu_make(pos)
257 for before_action in menu.actions():
257 for before_action in menu.actions():
258 if before_action.shortcut().matches(QtGui.QKeySequence.Paste) == \
258 if before_action.shortcut().matches(QtGui.QKeySequence.Paste) == \
259 QtGui.QKeySequence.ExactMatch:
259 QtGui.QKeySequence.ExactMatch:
260 menu.insertAction(before_action, self._copy_raw_action)
260 menu.insertAction(before_action, self._copy_raw_action)
261 break
261 break
262 return menu
262 return menu
263
263
264 def request_interrupt_kernel(self):
264 def request_interrupt_kernel(self):
265 if self._executing:
265 if self._executing:
266 self.interrupt_kernel()
266 self.interrupt_kernel()
267
267
268 def request_restart_kernel(self):
268 def request_restart_kernel(self):
269 message = 'Are you sure you want to restart the kernel?'
269 message = 'Are you sure you want to restart the kernel?'
270 self.restart_kernel(message, now=False)
270 self.restart_kernel(message, now=False)
271
271
272 def _event_filter_console_keypress(self, event):
272 def _event_filter_console_keypress(self, event):
273 """ Reimplemented for execution interruption and smart backspace.
273 """ Reimplemented for execution interruption and smart backspace.
274 """
274 """
275 key = event.key()
275 key = event.key()
276 if self._control_key_down(event.modifiers(), include_command=False):
276 if self._control_key_down(event.modifiers(), include_command=False):
277
277
278 if key == QtCore.Qt.Key_C and self._executing:
278 if key == QtCore.Qt.Key_C and self._executing:
279 self.request_interrupt_kernel()
279 self.request_interrupt_kernel()
280 return True
280 return True
281
281
282 elif key == QtCore.Qt.Key_Period:
282 elif key == QtCore.Qt.Key_Period:
283 self.request_restart_kernel()
283 self.request_restart_kernel()
284 return True
284 return True
285
285
286 elif not event.modifiers() & QtCore.Qt.AltModifier:
286 elif not event.modifiers() & QtCore.Qt.AltModifier:
287
287
288 # Smart backspace: remove four characters in one backspace if:
288 # Smart backspace: remove four characters in one backspace if:
289 # 1) everything left of the cursor is whitespace
289 # 1) everything left of the cursor is whitespace
290 # 2) the four characters immediately left of the cursor are spaces
290 # 2) the four characters immediately left of the cursor are spaces
291 if key == QtCore.Qt.Key_Backspace:
291 if key == QtCore.Qt.Key_Backspace:
292 col = self._get_input_buffer_cursor_column()
292 col = self._get_input_buffer_cursor_column()
293 cursor = self._control.textCursor()
293 cursor = self._control.textCursor()
294 if col > 3 and not cursor.hasSelection():
294 if col > 3 and not cursor.hasSelection():
295 text = self._get_input_buffer_cursor_line()[:col]
295 text = self._get_input_buffer_cursor_line()[:col]
296 if text.endswith(' ') and not text.strip():
296 if text.endswith(' ') and not text.strip():
297 cursor.movePosition(QtGui.QTextCursor.Left,
297 cursor.movePosition(QtGui.QTextCursor.Left,
298 QtGui.QTextCursor.KeepAnchor, 4)
298 QtGui.QTextCursor.KeepAnchor, 4)
299 cursor.removeSelectedText()
299 cursor.removeSelectedText()
300 return True
300 return True
301
301
302 return super(FrontendWidget, self)._event_filter_console_keypress(event)
302 return super(FrontendWidget, self)._event_filter_console_keypress(event)
303
303
304 def _insert_continuation_prompt(self, cursor):
304 def _insert_continuation_prompt(self, cursor):
305 """ Reimplemented for auto-indentation.
305 """ Reimplemented for auto-indentation.
306 """
306 """
307 super(FrontendWidget, self)._insert_continuation_prompt(cursor)
307 super(FrontendWidget, self)._insert_continuation_prompt(cursor)
308 cursor.insertText(' ' * self._input_splitter.indent_spaces)
308 cursor.insertText(' ' * self._input_splitter.indent_spaces)
309
309
310 #---------------------------------------------------------------------------
310 #---------------------------------------------------------------------------
311 # 'BaseFrontendMixin' abstract interface
311 # 'BaseFrontendMixin' abstract interface
312 #---------------------------------------------------------------------------
312 #---------------------------------------------------------------------------
313
313
314 def _handle_complete_reply(self, rep):
314 def _handle_complete_reply(self, rep):
315 """ Handle replies for tab completion.
315 """ Handle replies for tab completion.
316 """
316 """
317 self.log.debug("complete: %s", rep.get('content', ''))
317 self.log.debug("complete: %s", rep.get('content', ''))
318 cursor = self._get_cursor()
318 cursor = self._get_cursor()
319 info = self._request_info.get('complete')
319 info = self._request_info.get('complete')
320 if info and info.id == rep['parent_header']['msg_id'] and \
320 if info and info.id == rep['parent_header']['msg_id'] and \
321 info.pos == cursor.position():
321 info.pos == cursor.position():
322 text = '.'.join(self._get_context())
322 text = '.'.join(self._get_context())
323 cursor.movePosition(QtGui.QTextCursor.Left, n=len(text))
323 cursor.movePosition(QtGui.QTextCursor.Left, n=len(text))
324 self._complete_with_items(cursor, rep['content']['matches'])
324 self._complete_with_items(cursor, rep['content']['matches'])
325
325
326 def _silent_exec_callback(self, expr, callback):
326 def _silent_exec_callback(self, expr, callback):
327 """Silently execute `expr` in the kernel and call `callback` with reply
327 """Silently execute `expr` in the kernel and call `callback` with reply
328
328
329 the `expr` is evaluated silently in the kernel (without) output in
329 the `expr` is evaluated silently in the kernel (without) output in
330 the frontend. Call `callback` with the
330 the frontend. Call `callback` with the
331 `repr <http://docs.python.org/library/functions.html#repr> `_ as first argument
331 `repr <http://docs.python.org/library/functions.html#repr> `_ as first argument
332
332
333 Parameters
333 Parameters
334 ----------
334 ----------
335 expr : string
335 expr : string
336 valid string to be executed by the kernel.
336 valid string to be executed by the kernel.
337 callback : function
337 callback : function
338 function accepting one arguement, as a string. The string will be
338 function accepting one arguement, as a string. The string will be
339 the `repr` of the result of evaluating `expr`
339 the `repr` of the result of evaluating `expr`
340
340
341 The `callback` is called with the 'repr()' of the result of `expr` as
341 The `callback` is called with the 'repr()' of the result of `expr` as
342 first argument. To get the object, do 'eval()' onthe passed value.
342 first argument. To get the object, do 'eval()' onthe passed value.
343
343
344 See Also
344 See Also
345 --------
345 --------
346 _handle_exec_callback : private method, deal with calling callback with reply
346 _handle_exec_callback : private method, deal with calling callback with reply
347
347
348 """
348 """
349
349
350 # generate uuid, which would be used as a indication of wether or not
350 # generate uuid, which would be used as a indication of wether or not
351 # the unique request originate from here (can use msg id ?)
351 # the unique request originate from here (can use msg id ?)
352 local_uuid = str(uuid.uuid1())
352 local_uuid = str(uuid.uuid1())
353 msg_id = self.kernel_manager.shell_channel.execute('',
353 msg_id = self.kernel_manager.shell_channel.execute('',
354 silent=True, user_expressions={ local_uuid:expr })
354 silent=True, user_expressions={ local_uuid:expr })
355 self._callback_dict[local_uuid] = callback
355 self._callback_dict[local_uuid] = callback
356 self._request_info['execute'][msg_id] = self._ExecutionRequest(msg_id, 'silent_exec_callback')
356 self._request_info['execute'][msg_id] = self._ExecutionRequest(msg_id, 'silent_exec_callback')
357
357
358 def _handle_exec_callback(self, msg):
358 def _handle_exec_callback(self, msg):
359 """Execute `callback` corresonding to `msg` reply, after ``_silent_exec_callback``
359 """Execute `callback` corresonding to `msg` reply, after ``_silent_exec_callback``
360
360
361 Parameters
361 Parameters
362 ----------
362 ----------
363 msg : raw message send by the kernel containing an `user_expressions`
363 msg : raw message send by the kernel containing an `user_expressions`
364 and having a 'silent_exec_callback' kind.
364 and having a 'silent_exec_callback' kind.
365
365
366 Notes
366 Notes
367 -----
367 -----
368 This fonction will look for a `callback` associated with the
368 This fonction will look for a `callback` associated with the
369 corresponding message id. Association has been made by
369 corresponding message id. Association has been made by
370 `_silent_exec_callback`. `callback` is then called with the `repr()`
370 `_silent_exec_callback`. `callback` is then called with the `repr()`
371 of the value of corresponding `user_expressions` as argument.
371 of the value of corresponding `user_expressions` as argument.
372 `callback` is then removed from the known list so that any message
372 `callback` is then removed from the known list so that any message
373 coming again with the same id won't trigger it.
373 coming again with the same id won't trigger it.
374
374
375 """
375 """
376
376
377 user_exp = msg['content'].get('user_expressions')
377 user_exp = msg['content'].get('user_expressions')
378 if not user_exp:
378 if not user_exp:
379 return
379 return
380 for expression in user_exp:
380 for expression in user_exp:
381 if expression in self._callback_dict:
381 if expression in self._callback_dict:
382 self._callback_dict.pop(expression)(user_exp[expression])
382 self._callback_dict.pop(expression)(user_exp[expression])
383
383
384 def _handle_execute_reply(self, msg):
384 def _handle_execute_reply(self, msg):
385 """ Handles replies for code execution.
385 """ Handles replies for code execution.
386 """
386 """
387 self.log.debug("execute: %s", msg.get('content', ''))
387 self.log.debug("execute: %s", msg.get('content', ''))
388 msg_id = msg['parent_header']['msg_id']
388 msg_id = msg['parent_header']['msg_id']
389 info = self._request_info['execute'].get(msg_id)
389 info = self._request_info['execute'].get(msg_id)
390 # unset reading flag, because if execute finished, raw_input can't
390 # unset reading flag, because if execute finished, raw_input can't
391 # still be pending.
391 # still be pending.
392 self._reading = False
392 self._reading = False
393 if info and info.kind == 'user' and not self._hidden:
393 if info and info.kind == 'user' and not self._hidden:
394 # Make sure that all output from the SUB channel has been processed
394 # Make sure that all output from the SUB channel has been processed
395 # before writing a new prompt.
395 # before writing a new prompt.
396 self.kernel_manager.sub_channel.flush()
396 self.kernel_manager.sub_channel.flush()
397
397
398 # Reset the ANSI style information to prevent bad text in stdout
398 # Reset the ANSI style information to prevent bad text in stdout
399 # from messing up our colors. We're not a true terminal so we're
399 # from messing up our colors. We're not a true terminal so we're
400 # allowed to do this.
400 # allowed to do this.
401 if self.ansi_codes:
401 if self.ansi_codes:
402 self._ansi_processor.reset_sgr()
402 self._ansi_processor.reset_sgr()
403
403
404 content = msg['content']
404 content = msg['content']
405 status = content['status']
405 status = content['status']
406 if status == 'ok':
406 if status == 'ok':
407 self._process_execute_ok(msg)
407 self._process_execute_ok(msg)
408 elif status == 'error':
408 elif status == 'error':
409 self._process_execute_error(msg)
409 self._process_execute_error(msg)
410 elif status == 'aborted':
410 elif status == 'aborted':
411 self._process_execute_abort(msg)
411 self._process_execute_abort(msg)
412
412
413 self._show_interpreter_prompt_for_reply(msg)
413 self._show_interpreter_prompt_for_reply(msg)
414 self.executed.emit(msg)
414 self.executed.emit(msg)
415 self._request_info['execute'].pop(msg_id)
415 self._request_info['execute'].pop(msg_id)
416 elif info and info.kind == 'silent_exec_callback' and not self._hidden:
416 elif info and info.kind == 'silent_exec_callback' and not self._hidden:
417 self._handle_exec_callback(msg)
417 self._handle_exec_callback(msg)
418 self._request_info['execute'].pop(msg_id)
418 self._request_info['execute'].pop(msg_id)
419 else:
419 else:
420 super(FrontendWidget, self)._handle_execute_reply(msg)
420 super(FrontendWidget, self)._handle_execute_reply(msg)
421
421
422 def _handle_input_request(self, msg):
422 def _handle_input_request(self, msg):
423 """ Handle requests for raw_input.
423 """ Handle requests for raw_input.
424 """
424 """
425 self.log.debug("input: %s", msg.get('content', ''))
425 self.log.debug("input: %s", msg.get('content', ''))
426 if self._hidden:
426 if self._hidden:
427 raise RuntimeError('Request for raw input during hidden execution.')
427 raise RuntimeError('Request for raw input during hidden execution.')
428
428
429 # Make sure that all output from the SUB channel has been processed
429 # Make sure that all output from the SUB channel has been processed
430 # before entering readline mode.
430 # before entering readline mode.
431 self.kernel_manager.sub_channel.flush()
431 self.kernel_manager.sub_channel.flush()
432
432
433 def callback(line):
433 def callback(line):
434 self.kernel_manager.stdin_channel.input(line)
434 self.kernel_manager.stdin_channel.input(line)
435 if self._reading:
435 if self._reading:
436 self.log.debug("Got second input request, assuming first was interrupted.")
436 self.log.debug("Got second input request, assuming first was interrupted.")
437 self._reading = False
437 self._reading = False
438 self._readline(msg['content']['prompt'], callback=callback)
438 self._readline(msg['content']['prompt'], callback=callback)
439
439
440 def _handle_kernel_died(self, since_last_heartbeat):
440 def _handle_kernel_died(self, since_last_heartbeat):
441 """ Handle the kernel's death by asking if the user wants to restart.
441 """ Handle the kernel's death by asking if the user wants to restart.
442 """
442 """
443 self.log.debug("kernel died: %s", since_last_heartbeat)
443 self.log.debug("kernel died: %s", since_last_heartbeat)
444 if self.custom_restart:
444 if self.custom_restart:
445 self.custom_restart_kernel_died.emit(since_last_heartbeat)
445 self.custom_restart_kernel_died.emit(since_last_heartbeat)
446 else:
446 else:
447 message = 'The kernel heartbeat has been inactive for %.2f ' \
447 message = 'The kernel heartbeat has been inactive for %.2f ' \
448 'seconds. Do you want to restart the kernel? You may ' \
448 'seconds. Do you want to restart the kernel? You may ' \
449 'first want to check the network connection.' % \
449 'first want to check the network connection.' % \
450 since_last_heartbeat
450 since_last_heartbeat
451 self.restart_kernel(message, now=True)
451 self.restart_kernel(message, now=True)
452
452
453 def _handle_object_info_reply(self, rep):
453 def _handle_object_info_reply(self, rep):
454 """ Handle replies for call tips.
454 """ Handle replies for call tips.
455 """
455 """
456 self.log.debug("oinfo: %s", rep.get('content', ''))
456 self.log.debug("oinfo: %s", rep.get('content', ''))
457 cursor = self._get_cursor()
457 cursor = self._get_cursor()
458 info = self._request_info.get('call_tip')
458 info = self._request_info.get('call_tip')
459 if info and info.id == rep['parent_header']['msg_id'] and \
459 if info and info.id == rep['parent_header']['msg_id'] and \
460 info.pos == cursor.position():
460 info.pos == cursor.position():
461 # Get the information for a call tip. For now we format the call
461 # Get the information for a call tip. For now we format the call
462 # line as string, later we can pass False to format_call and
462 # line as string, later we can pass False to format_call and
463 # syntax-highlight it ourselves for nicer formatting in the
463 # syntax-highlight it ourselves for nicer formatting in the
464 # calltip.
464 # calltip.
465 content = rep['content']
465 content = rep['content']
466 # if this is from pykernel, 'docstring' will be the only key
466 # if this is from pykernel, 'docstring' will be the only key
467 if content.get('ismagic', False):
467 if content.get('ismagic', False):
468 # Don't generate a call-tip for magics. Ideally, we should
468 # Don't generate a call-tip for magics. Ideally, we should
469 # generate a tooltip, but not on ( like we do for actual
469 # generate a tooltip, but not on ( like we do for actual
470 # callables.
470 # callables.
471 call_info, doc = None, None
471 call_info, doc = None, None
472 else:
472 else:
473 call_info, doc = call_tip(content, format_call=True)
473 call_info, doc = call_tip(content, format_call=True)
474 if call_info or doc:
474 if call_info or doc:
475 self._call_tip_widget.show_call_info(call_info, doc)
475 self._call_tip_widget.show_call_info(call_info, doc)
476
476
477 def _handle_pyout(self, msg):
477 def _handle_pyout(self, msg):
478 """ Handle display hook output.
478 """ Handle display hook output.
479 """
479 """
480 self.log.debug("pyout: %s", msg.get('content', ''))
480 self.log.debug("pyout: %s", msg.get('content', ''))
481 if not self._hidden and self._is_from_this_session(msg):
481 if not self._hidden and self._is_from_this_session(msg):
482 text = msg['content']['data']
482 text = msg['content']['data']
483 self._append_plain_text(text + '\n', before_prompt=True)
483 self._append_plain_text(text + '\n', before_prompt=True)
484
484
485 def _handle_stream(self, msg):
485 def _handle_stream(self, msg):
486 """ Handle stdout, stderr, and stdin.
486 """ Handle stdout, stderr, and stdin.
487 """
487 """
488 self.log.debug("stream: %s", msg.get('content', ''))
488 self.log.debug("stream: %s", msg.get('content', ''))
489 if not self._hidden and self._is_from_this_session(msg):
489 if not self._hidden and self._is_from_this_session(msg):
490 # Most consoles treat tabs as being 8 space characters. Convert tabs
490 # Most consoles treat tabs as being 8 space characters. Convert tabs
491 # to spaces so that output looks as expected regardless of this
491 # to spaces so that output looks as expected regardless of this
492 # widget's tab width.
492 # widget's tab width.
493 text = msg['content']['data'].expandtabs(8)
493 text = msg['content']['data'].expandtabs(8)
494
494
495 self._append_plain_text(text, before_prompt=True)
495 self._append_plain_text(text, before_prompt=True)
496 self._control.moveCursor(QtGui.QTextCursor.End)
496 self._control.moveCursor(QtGui.QTextCursor.End)
497
497
498 def _handle_shutdown_reply(self, msg):
498 def _handle_shutdown_reply(self, msg):
499 """ Handle shutdown signal, only if from other console.
499 """ Handle shutdown signal, only if from other console.
500 """
500 """
501 self.log.debug("shutdown: %s", msg.get('content', ''))
501 self.log.debug("shutdown: %s", msg.get('content', ''))
502 if not self._hidden and not self._is_from_this_session(msg):
502 if not self._hidden and not self._is_from_this_session(msg):
503 if self._local_kernel:
503 if self._local_kernel:
504 if not msg['content']['restart']:
504 if not msg['content']['restart']:
505 self.exit_requested.emit(self)
505 self.exit_requested.emit(self)
506 else:
506 else:
507 # we just got notified of a restart!
507 # we just got notified of a restart!
508 time.sleep(0.25) # wait 1/4 sec to reset
508 time.sleep(0.25) # wait 1/4 sec to reset
509 # lest the request for a new prompt
509 # lest the request for a new prompt
510 # goes to the old kernel
510 # goes to the old kernel
511 self.reset()
511 self.reset()
512 else: # remote kernel, prompt on Kernel shutdown/reset
512 else: # remote kernel, prompt on Kernel shutdown/reset
513 title = self.window().windowTitle()
513 title = self.window().windowTitle()
514 if not msg['content']['restart']:
514 if not msg['content']['restart']:
515 reply = QtGui.QMessageBox.question(self, title,
515 reply = QtGui.QMessageBox.question(self, title,
516 "Kernel has been shutdown permanently. "
516 "Kernel has been shutdown permanently. "
517 "Close the Console?",
517 "Close the Console?",
518 QtGui.QMessageBox.Yes,QtGui.QMessageBox.No)
518 QtGui.QMessageBox.Yes,QtGui.QMessageBox.No)
519 if reply == QtGui.QMessageBox.Yes:
519 if reply == QtGui.QMessageBox.Yes:
520 self.exit_requested.emit(self)
520 self.exit_requested.emit(self)
521 else:
521 else:
522 # XXX: remove message box in favor of using the
522 # XXX: remove message box in favor of using the
523 # clear_on_kernel_restart setting?
523 # clear_on_kernel_restart setting?
524 reply = QtGui.QMessageBox.question(self, title,
524 reply = QtGui.QMessageBox.question(self, title,
525 "Kernel has been reset. Clear the Console?",
525 "Kernel has been reset. Clear the Console?",
526 QtGui.QMessageBox.Yes,QtGui.QMessageBox.No)
526 QtGui.QMessageBox.Yes,QtGui.QMessageBox.No)
527 if reply == QtGui.QMessageBox.Yes:
527 if reply == QtGui.QMessageBox.Yes:
528 time.sleep(0.25) # wait 1/4 sec to reset
528 time.sleep(0.25) # wait 1/4 sec to reset
529 # lest the request for a new prompt
529 # lest the request for a new prompt
530 # goes to the old kernel
530 # goes to the old kernel
531 self.reset()
531 self.reset()
532
532
533 def _started_channels(self):
533 def _started_channels(self):
534 """ Called when the KernelManager channels have started listening or
534 """ Called when the KernelManager channels have started listening or
535 when the frontend is assigned an already listening KernelManager.
535 when the frontend is assigned an already listening KernelManager.
536 """
536 """
537 self.reset(force=True)
537 self.reset(clear=True)
538
538
539 #---------------------------------------------------------------------------
539 #---------------------------------------------------------------------------
540 # 'FrontendWidget' public interface
540 # 'FrontendWidget' public interface
541 #---------------------------------------------------------------------------
541 #---------------------------------------------------------------------------
542
542
543 def copy_raw(self):
543 def copy_raw(self):
544 """ Copy the currently selected text to the clipboard without attempting
544 """ Copy the currently selected text to the clipboard without attempting
545 to remove prompts or otherwise alter the text.
545 to remove prompts or otherwise alter the text.
546 """
546 """
547 self._control.copy()
547 self._control.copy()
548
548
549 def execute_file(self, path, hidden=False):
549 def execute_file(self, path, hidden=False):
550 """ Attempts to execute file with 'path'. If 'hidden', no output is
550 """ Attempts to execute file with 'path'. If 'hidden', no output is
551 shown.
551 shown.
552 """
552 """
553 self.execute('execfile(%r)' % path, hidden=hidden)
553 self.execute('execfile(%r)' % path, hidden=hidden)
554
554
555 def interrupt_kernel(self):
555 def interrupt_kernel(self):
556 """ Attempts to interrupt the running kernel.
556 """ Attempts to interrupt the running kernel.
557
557
558 Also unsets _reading flag, to avoid runtime errors
558 Also unsets _reading flag, to avoid runtime errors
559 if raw_input is called again.
559 if raw_input is called again.
560 """
560 """
561 if self.custom_interrupt:
561 if self.custom_interrupt:
562 self._reading = False
562 self._reading = False
563 self.custom_interrupt_requested.emit()
563 self.custom_interrupt_requested.emit()
564 elif self.kernel_manager.has_kernel:
564 elif self.kernel_manager.has_kernel:
565 self._reading = False
565 self._reading = False
566 self.kernel_manager.interrupt_kernel()
566 self.kernel_manager.interrupt_kernel()
567 else:
567 else:
568 self._append_plain_text('Kernel process is either remote or '
568 self._append_plain_text('Kernel process is either remote or '
569 'unspecified. Cannot interrupt.\n')
569 'unspecified. Cannot interrupt.\n')
570
570
571 def reset(self, force=False):
571 def reset(self, clear=False):
572 """ Resets the widget to its initial state if ``force`` parameter or
572 """ Resets the widget to its initial state if ``clear`` parameter or
573 ``clear_on_kernel_restart`` configuration setting is True, otherwise
573 ``clear_on_kernel_restart`` configuration setting is True, otherwise
574 prints a visual indication of the fact that the kernel restarted, but
574 prints a visual indication of the fact that the kernel restarted, but
575 does not clear the traces from previous usage of the kernel before it
575 does not clear the traces from previous usage of the kernel before it
576 was restarted. With ``force=True``, it is similar to ``clear``, but
576 was restarted. With ``clear=True``, it is similar to ``%clear``, but
577 also re-writes the banner and aborts execution if necessary.
577 also re-writes the banner and aborts execution if necessary.
578 """
578 """
579 if self._executing:
579 if self._executing:
580 self._executing = False
580 self._executing = False
581 self._request_info['execute'] = {}
581 self._request_info['execute'] = {}
582 self._reading = False
582 self._reading = False
583 self._highlighter.highlighting_on = False
583 self._highlighter.highlighting_on = False
584
584
585 if self.clear_on_kernel_restart or force:
585 if self.clear_on_kernel_restart or clear:
586 self._control.clear()
586 self._control.clear()
587 self._append_plain_text(self.banner)
587 self._append_plain_text(self.banner)
588 else:
588 else:
589 self._append_plain_text("# restarting kernel...")
589 self._append_plain_text("# restarting kernel...")
590 self._append_html("<hr><br>")
590 self._append_html("<hr><br>")
591 # XXX: Reprinting the full banner may be too much, but once #1680 is
591 # XXX: Reprinting the full banner may be too much, but once #1680 is
592 # addressed, that will mitigate it.
592 # addressed, that will mitigate it.
593 #self._append_plain_text(self.banner)
593 #self._append_plain_text(self.banner)
594 # update output marker for stdout/stderr, so that startup
594 # update output marker for stdout/stderr, so that startup
595 # messages appear after banner:
595 # messages appear after banner:
596 self._append_before_prompt_pos = self._get_cursor().position()
596 self._append_before_prompt_pos = self._get_cursor().position()
597 self._show_interpreter_prompt()
597 self._show_interpreter_prompt()
598
598
599 def restart_kernel(self, message, now=False):
599 def restart_kernel(self, message, now=False):
600 """ Attempts to restart the running kernel.
600 """ Attempts to restart the running kernel.
601 """
601 """
602 # FIXME: now should be configurable via a checkbox in the dialog. Right
602 # FIXME: now should be configurable via a checkbox in the dialog. Right
603 # now at least the heartbeat path sets it to True and the manual restart
603 # now at least the heartbeat path sets it to True and the manual restart
604 # to False. But those should just be the pre-selected states of a
604 # to False. But those should just be the pre-selected states of a
605 # checkbox that the user could override if so desired. But I don't know
605 # checkbox that the user could override if so desired. But I don't know
606 # enough Qt to go implementing the checkbox now.
606 # enough Qt to go implementing the checkbox now.
607
607
608 if self.custom_restart:
608 if self.custom_restart:
609 self.custom_restart_requested.emit()
609 self.custom_restart_requested.emit()
610
610
611 elif self.kernel_manager.has_kernel:
611 elif self.kernel_manager.has_kernel:
612 # Pause the heart beat channel to prevent further warnings.
612 # Pause the heart beat channel to prevent further warnings.
613 self.kernel_manager.hb_channel.pause()
613 self.kernel_manager.hb_channel.pause()
614
614
615 # Prompt the user to restart the kernel. Un-pause the heartbeat if
615 # Prompt the user to restart the kernel. Un-pause the heartbeat if
616 # they decline. (If they accept, the heartbeat will be un-paused
616 # they decline. (If they accept, the heartbeat will be un-paused
617 # automatically when the kernel is restarted.)
617 # automatically when the kernel is restarted.)
618 buttons = QtGui.QMessageBox.Yes | QtGui.QMessageBox.No
618 buttons = QtGui.QMessageBox.Yes | QtGui.QMessageBox.No
619 result = QtGui.QMessageBox.question(self, 'Restart kernel?',
619 result = QtGui.QMessageBox.question(self, 'Restart kernel?',
620 message, buttons)
620 message, buttons)
621 if result == QtGui.QMessageBox.Yes:
621 if result == QtGui.QMessageBox.Yes:
622 try:
622 try:
623 self.kernel_manager.restart_kernel(now=now)
623 self.kernel_manager.restart_kernel(now=now)
624 except RuntimeError:
624 except RuntimeError:
625 self._append_plain_text('Kernel started externally. '
625 self._append_plain_text('Kernel started externally. '
626 'Cannot restart.\n',
626 'Cannot restart.\n',
627 before_prompt=True
627 before_prompt=True
628 )
628 )
629 else:
629 else:
630 self.reset()
630 self.reset()
631 else:
631 else:
632 self.kernel_manager.hb_channel.unpause()
632 self.kernel_manager.hb_channel.unpause()
633
633
634 else:
634 else:
635 self._append_plain_text('Kernel process is either remote or '
635 self._append_plain_text('Kernel process is either remote or '
636 'unspecified. Cannot restart.\n',
636 'unspecified. Cannot restart.\n',
637 before_prompt=True
637 before_prompt=True
638 )
638 )
639
639
640 #---------------------------------------------------------------------------
640 #---------------------------------------------------------------------------
641 # 'FrontendWidget' protected interface
641 # 'FrontendWidget' protected interface
642 #---------------------------------------------------------------------------
642 #---------------------------------------------------------------------------
643
643
644 def _call_tip(self):
644 def _call_tip(self):
645 """ Shows a call tip, if appropriate, at the current cursor location.
645 """ Shows a call tip, if appropriate, at the current cursor location.
646 """
646 """
647 # Decide if it makes sense to show a call tip
647 # Decide if it makes sense to show a call tip
648 if not self.enable_calltips:
648 if not self.enable_calltips:
649 return False
649 return False
650 cursor = self._get_cursor()
650 cursor = self._get_cursor()
651 cursor.movePosition(QtGui.QTextCursor.Left)
651 cursor.movePosition(QtGui.QTextCursor.Left)
652 if cursor.document().characterAt(cursor.position()) != '(':
652 if cursor.document().characterAt(cursor.position()) != '(':
653 return False
653 return False
654 context = self._get_context(cursor)
654 context = self._get_context(cursor)
655 if not context:
655 if not context:
656 return False
656 return False
657
657
658 # Send the metadata request to the kernel
658 # Send the metadata request to the kernel
659 name = '.'.join(context)
659 name = '.'.join(context)
660 msg_id = self.kernel_manager.shell_channel.object_info(name)
660 msg_id = self.kernel_manager.shell_channel.object_info(name)
661 pos = self._get_cursor().position()
661 pos = self._get_cursor().position()
662 self._request_info['call_tip'] = self._CallTipRequest(msg_id, pos)
662 self._request_info['call_tip'] = self._CallTipRequest(msg_id, pos)
663 return True
663 return True
664
664
665 def _complete(self):
665 def _complete(self):
666 """ Performs completion at the current cursor location.
666 """ Performs completion at the current cursor location.
667 """
667 """
668 context = self._get_context()
668 context = self._get_context()
669 if context:
669 if context:
670 # Send the completion request to the kernel
670 # Send the completion request to the kernel
671 msg_id = self.kernel_manager.shell_channel.complete(
671 msg_id = self.kernel_manager.shell_channel.complete(
672 '.'.join(context), # text
672 '.'.join(context), # text
673 self._get_input_buffer_cursor_line(), # line
673 self._get_input_buffer_cursor_line(), # line
674 self._get_input_buffer_cursor_column(), # cursor_pos
674 self._get_input_buffer_cursor_column(), # cursor_pos
675 self.input_buffer) # block
675 self.input_buffer) # block
676 pos = self._get_cursor().position()
676 pos = self._get_cursor().position()
677 info = self._CompletionRequest(msg_id, pos)
677 info = self._CompletionRequest(msg_id, pos)
678 self._request_info['complete'] = info
678 self._request_info['complete'] = info
679
679
680 def _get_context(self, cursor=None):
680 def _get_context(self, cursor=None):
681 """ Gets the context for the specified cursor (or the current cursor
681 """ Gets the context for the specified cursor (or the current cursor
682 if none is specified).
682 if none is specified).
683 """
683 """
684 if cursor is None:
684 if cursor is None:
685 cursor = self._get_cursor()
685 cursor = self._get_cursor()
686 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
686 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
687 QtGui.QTextCursor.KeepAnchor)
687 QtGui.QTextCursor.KeepAnchor)
688 text = cursor.selection().toPlainText()
688 text = cursor.selection().toPlainText()
689 return self._completion_lexer.get_context(text)
689 return self._completion_lexer.get_context(text)
690
690
691 def _process_execute_abort(self, msg):
691 def _process_execute_abort(self, msg):
692 """ Process a reply for an aborted execution request.
692 """ Process a reply for an aborted execution request.
693 """
693 """
694 self._append_plain_text("ERROR: execution aborted\n")
694 self._append_plain_text("ERROR: execution aborted\n")
695
695
696 def _process_execute_error(self, msg):
696 def _process_execute_error(self, msg):
697 """ Process a reply for an execution request that resulted in an error.
697 """ Process a reply for an execution request that resulted in an error.
698 """
698 """
699 content = msg['content']
699 content = msg['content']
700 # If a SystemExit is passed along, this means exit() was called - also
700 # If a SystemExit is passed along, this means exit() was called - also
701 # all the ipython %exit magic syntax of '-k' to be used to keep
701 # all the ipython %exit magic syntax of '-k' to be used to keep
702 # the kernel running
702 # the kernel running
703 if content['ename']=='SystemExit':
703 if content['ename']=='SystemExit':
704 keepkernel = content['evalue']=='-k' or content['evalue']=='True'
704 keepkernel = content['evalue']=='-k' or content['evalue']=='True'
705 self._keep_kernel_on_exit = keepkernel
705 self._keep_kernel_on_exit = keepkernel
706 self.exit_requested.emit(self)
706 self.exit_requested.emit(self)
707 else:
707 else:
708 traceback = ''.join(content['traceback'])
708 traceback = ''.join(content['traceback'])
709 self._append_plain_text(traceback)
709 self._append_plain_text(traceback)
710
710
711 def _process_execute_ok(self, msg):
711 def _process_execute_ok(self, msg):
712 """ Process a reply for a successful execution request.
712 """ Process a reply for a successful execution request.
713 """
713 """
714 payload = msg['content']['payload']
714 payload = msg['content']['payload']
715 for item in payload:
715 for item in payload:
716 if not self._process_execute_payload(item):
716 if not self._process_execute_payload(item):
717 warning = 'Warning: received unknown payload of type %s'
717 warning = 'Warning: received unknown payload of type %s'
718 print(warning % repr(item['source']))
718 print(warning % repr(item['source']))
719
719
720 def _process_execute_payload(self, item):
720 def _process_execute_payload(self, item):
721 """ Process a single payload item from the list of payload items in an
721 """ Process a single payload item from the list of payload items in an
722 execution reply. Returns whether the payload was handled.
722 execution reply. Returns whether the payload was handled.
723 """
723 """
724 # The basic FrontendWidget doesn't handle payloads, as they are a
724 # The basic FrontendWidget doesn't handle payloads, as they are a
725 # mechanism for going beyond the standard Python interpreter model.
725 # mechanism for going beyond the standard Python interpreter model.
726 return False
726 return False
727
727
728 def _show_interpreter_prompt(self):
728 def _show_interpreter_prompt(self):
729 """ Shows a prompt for the interpreter.
729 """ Shows a prompt for the interpreter.
730 """
730 """
731 self._show_prompt('>>> ')
731 self._show_prompt('>>> ')
732
732
733 def _show_interpreter_prompt_for_reply(self, msg):
733 def _show_interpreter_prompt_for_reply(self, msg):
734 """ Shows a prompt for the interpreter given an 'execute_reply' message.
734 """ Shows a prompt for the interpreter given an 'execute_reply' message.
735 """
735 """
736 self._show_interpreter_prompt()
736 self._show_interpreter_prompt()
737
737
738 #------ Signal handlers ----------------------------------------------------
738 #------ Signal handlers ----------------------------------------------------
739
739
740 def _document_contents_change(self, position, removed, added):
740 def _document_contents_change(self, position, removed, added):
741 """ Called whenever the document's content changes. Display a call tip
741 """ Called whenever the document's content changes. Display a call tip
742 if appropriate.
742 if appropriate.
743 """
743 """
744 # Calculate where the cursor should be *after* the change:
744 # Calculate where the cursor should be *after* the change:
745 position += added
745 position += added
746
746
747 document = self._control.document()
747 document = self._control.document()
748 if position == self._get_cursor().position():
748 if position == self._get_cursor().position():
749 self._call_tip()
749 self._call_tip()
750
750
751 #------ Trait default initializers -----------------------------------------
751 #------ Trait default initializers -----------------------------------------
752
752
753 def _banner_default(self):
753 def _banner_default(self):
754 """ Returns the standard Python banner.
754 """ Returns the standard Python banner.
755 """
755 """
756 banner = 'Python %s on %s\nType "help", "copyright", "credits" or ' \
756 banner = 'Python %s on %s\nType "help", "copyright", "credits" or ' \
757 '"license" for more information.'
757 '"license" for more information.'
758 return banner % (sys.version, sys.platform)
758 return banner % (sys.version, sys.platform)
General Comments 0
You need to be logged in to leave comments. Login now