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