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