##// END OF EJS Templates
Merge branch 'newkernel' of github.com:ipython/ipython into upstream-newkernel
Brian Granger -
r2901:61ddddd9 merge
parent child Browse files
Show More
@@ -0,0 +1,101 b''
1 """ Provides bracket matching for Q[Plain]TextEdit widgets.
2 """
3
4 # System library imports
5 from PyQt4 import QtCore, QtGui
6
7
8 class BracketMatcher(QtCore.QObject):
9 """ Matches square brackets, braces, and parentheses based on cursor
10 position.
11 """
12
13 # Protected class variables.
14 _opening_map = { '(':')', '{':'}', '[':']' }
15 _closing_map = { ')':'(', '}':'{', ']':'[' }
16
17 #--------------------------------------------------------------------------
18 # 'QObject' interface
19 #--------------------------------------------------------------------------
20
21 def __init__(self, parent):
22 """ Create a call tip manager that is attached to the specified Qt
23 text edit widget.
24 """
25 assert isinstance(parent, (QtGui.QTextEdit, QtGui.QPlainTextEdit))
26 QtCore.QObject.__init__(self, parent)
27
28 # The format to apply to matching brackets.
29 self.format = QtGui.QTextCharFormat()
30 self.format.setBackground(QtGui.QColor('silver'))
31
32 parent.cursorPositionChanged.connect(self._cursor_position_changed)
33
34 #--------------------------------------------------------------------------
35 # Protected interface
36 #--------------------------------------------------------------------------
37
38 def _find_match(self, position):
39 """ Given a valid position in the text document, try to find the
40 position of the matching bracket. Returns -1 if unsuccessful.
41 """
42 # Decide what character to search for and what direction to search in.
43 document = self.parent().document()
44 qchar = document.characterAt(position)
45 start_char = qchar.toAscii()
46 search_char = self._opening_map.get(start_char)
47 if search_char:
48 increment = 1
49 else:
50 search_char = self._closing_map.get(start_char)
51 if search_char:
52 increment = -1
53 else:
54 return -1
55
56 # Search for the character.
57 depth = 0
58 while position >= 0 and position < document.characterCount():
59 char = qchar.toAscii()
60 if char == start_char:
61 depth += 1
62 elif char == search_char:
63 depth -= 1
64 if depth == 0:
65 break
66 position += increment
67 qchar = document.characterAt(position)
68 else:
69 position = -1
70 return position
71
72 def _selection_for_character(self, position):
73 """ Convenience method for selecting a character.
74 """
75 selection = QtGui.QTextEdit.ExtraSelection()
76 cursor = self.parent().textCursor()
77 cursor.setPosition(position)
78 cursor.movePosition(QtGui.QTextCursor.NextCharacter,
79 QtGui.QTextCursor.KeepAnchor)
80 selection.cursor = cursor
81 selection.format = self.format
82 return selection
83
84 #------ Signal handlers ----------------------------------------------------
85
86 def _cursor_position_changed(self):
87 """ Updates the document formatting based on the new cursor position.
88 """
89 # Clear out the old formatting.
90 text_edit = self.parent()
91 text_edit.setExtraSelections([])
92
93 # Attempt to match a bracket for the new cursor position.
94 cursor = text_edit.textCursor()
95 if not cursor.hasSelection():
96 position = cursor.position() - 1
97 match_position = self._find_match(position)
98 if match_position != -1:
99 extra_selections = [ self._selection_for_character(pos)
100 for pos in (position, match_position) ]
101 text_edit.setExtraSelections(extra_selections)
@@ -35,14 +35,14 b" def magic_history(self, parameter_s = ''):"
35 35 for making documentation, and in conjunction with -o, for producing
36 36 doctest-ready output.
37 37
38 -t: (default) print the 'translated' history, as IPython understands it.
39 IPython filters your input and converts it all into valid Python source
40 before executing it (things like magics or aliases are turned into
41 function calls, for example). With this option, you'll see the native
42 history instead of the user-entered version: '%cd /' will be seen as
43 '_ip.magic("%cd /")' instead of '%cd /'.
38 -r: (default) print the 'raw' history, i.e. the actual commands you typed.
44 39
45 -r: print the 'raw' history, i.e. the actual commands you typed.
40 -t: print the 'translated' history, as IPython understands it. IPython
41 filters your input and converts it all into valid Python source before
42 executing it (things like magics or aliases are turned into function
43 calls, for example). With this option, you'll see the native history
44 instead of the user-entered version: '%cd /' will be seen as
45 'get_ipython().magic("%cd /")' instead of '%cd /'.
46 46
47 47 -g: treat the arg as a pattern to grep for in (full) history.
48 48 This includes the "shadow history" (almost all commands ever written).
@@ -80,7 +80,8 b" def magic_history(self, parameter_s = ''):"
80 80 elif 'r' in opts:
81 81 input_hist = self.input_hist_raw
82 82 else:
83 input_hist = self.input_hist
83 # Raw history is the default
84 input_hist = self.input_hist_raw
84 85
85 86 default_length = 40
86 87 pattern = None
@@ -1828,7 +1828,6 b' class InteractiveShell(Configurable, Magic):'
1828 1828 self.resetbuffer()
1829 1829 lines = lines.splitlines()
1830 1830 more = 0
1831
1832 1831 with nested(self.builtin_trap, self.display_trap):
1833 1832 for line in lines:
1834 1833 # skip blank lines so we don't mess up the prompt counter, but do
@@ -1837,8 +1836,9 b' class InteractiveShell(Configurable, Magic):'
1837 1836
1838 1837 if line or more:
1839 1838 # push to raw history, so hist line numbers stay in sync
1840 self.input_hist_raw.append("# " + line + "\n")
1841 prefiltered = self.prefilter_manager.prefilter_lines(line,more)
1839 self.input_hist_raw.append(line + '\n')
1840 prefiltered = self.prefilter_manager.prefilter_lines(line,
1841 more)
1842 1842 more = self.push_line(prefiltered)
1843 1843 # IPython's runsource returns None if there was an error
1844 1844 # compiling the code. This allows us to stop processing right
@@ -101,7 +101,7 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
101 101 QtCore.Qt.Key_N : QtCore.Qt.Key_Down,
102 102 QtCore.Qt.Key_D : QtCore.Qt.Key_Delete, }
103 103 _shortcuts = set(_ctrl_down_remap.keys() +
104 [ QtCore.Qt.Key_C, QtCore.Qt.Key_V ])
104 [ QtCore.Qt.Key_C, QtCore.Qt.Key_V, QtCore.Qt.Key_O ])
105 105
106 106 #---------------------------------------------------------------------------
107 107 # 'QObject' interface
@@ -164,18 +164,26 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
164 164
165 165 def eventFilter(self, obj, event):
166 166 """ Reimplemented to ensure a console-like behavior in the underlying
167 text widget.
167 text widgets.
168 168 """
169 # Re-map keys for all filtered widgets.
170 169 etype = event.type()
171 if etype == QtCore.QEvent.KeyPress and \
172 self._control_key_down(event.modifiers()) and \
173 event.key() in self._ctrl_down_remap:
174 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
175 self._ctrl_down_remap[event.key()],
176 QtCore.Qt.NoModifier)
177 QtGui.qApp.sendEvent(obj, new_event)
178 return True
170 if etype == QtCore.QEvent.KeyPress:
171
172 # Re-map keys for all filtered widgets.
173 key = event.key()
174 if self._control_key_down(event.modifiers()) and \
175 key in self._ctrl_down_remap:
176 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
177 self._ctrl_down_remap[key],
178 QtCore.Qt.NoModifier)
179 QtGui.qApp.sendEvent(obj, new_event)
180 return True
181
182 elif obj == self._control:
183 return self._event_filter_console_keypress(event)
184
185 elif obj == self._page_control:
186 return self._event_filter_page_keypress(event)
179 187
180 188 # Override shortucts for all filtered widgets. Note that on Mac OS it is
181 189 # always unnecessary to override shortcuts, hence the check below (users
@@ -187,12 +195,6 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
187 195 event.accept()
188 196 return False
189 197
190 elif etype == QtCore.QEvent.KeyPress:
191 if obj == self._control:
192 return self._event_filter_console_keypress(event)
193 elif obj == self._page_control:
194 return self._event_filter_page_keypress(event)
195
196 198 return super(ConsoleWidget, self).eventFilter(obj, event)
197 199
198 200 #---------------------------------------------------------------------------
@@ -334,12 +336,11 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
334 336 else:
335 337 # Do this inside an edit block so continuation prompts are
336 338 # removed seamlessly via undo/redo.
337 cursor = self._control.textCursor()
339 cursor = self._get_end_cursor()
338 340 cursor.beginEditBlock()
339
340 self._append_plain_text('\n')
341 self._show_continuation_prompt()
342
341 cursor.insertText('\n')
342 self._insert_continuation_prompt(cursor)
343 self._control.moveCursor(QtGui.QTextCursor.End)
343 344 cursor.endEditBlock()
344 345
345 346 return complete
@@ -431,9 +432,8 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
431 432 """ Sets the font to the default fixed-width font for this platform.
432 433 """
433 434 # FIXME: font family and size should be configurable by the user.
434
435 435 if sys.platform == 'win32':
436 # Fixme: we should test whether Consolas is available and use it
436 # FIXME: we should test whether Consolas is available and use it
437 437 # first if it is. Consolas ships by default from Vista onwards,
438 438 # it's *vastly* more readable and prettier than Courier, and is
439 439 # often installed even on XP systems. So we should first check for
@@ -632,13 +632,32 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
632 632 if self._in_buffer(position):
633 633 cursor.movePosition(QtGui.QTextCursor.EndOfLine,
634 634 QtGui.QTextCursor.KeepAnchor)
635 if not cursor.hasSelection():
636 # Line deletion (remove continuation prompt)
637 cursor.movePosition(QtGui.QTextCursor.NextBlock,
638 QtGui.QTextCursor.KeepAnchor)
639 cursor.movePosition(QtGui.QTextCursor.Right,
640 QtGui.QTextCursor.KeepAnchor,
641 len(self._continuation_prompt))
635 642 cursor.removeSelectedText()
636 643 intercepted = True
637 644
638 645 elif key == QtCore.Qt.Key_L:
646 # It would be better to simply move the prompt block to the top
647 # of the control viewport. QPlainTextEdit has a private method
648 # to do this (setTopBlock), but it cannot be duplicated here
649 # because it requires access to the QTextControl that underlies
650 # both QPlainTextEdit and QTextEdit. In short, this can only be
651 # achieved by appending newlines after the prompt, which is a
652 # gigantic hack and likely to cause other problems.
639 653 self.clear()
640 654 intercepted = True
641 655
656 elif key == QtCore.Qt.Key_O:
657 if self._page_control and self._page_control.isVisible():
658 self._page_control.setFocus()
659 intercept = True
660
642 661 elif key == QtCore.Qt.Key_X:
643 662 # FIXME: Instead of disabling cut completely, only allow it
644 663 # when safe.
@@ -669,16 +688,44 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
669 688 cursor.removeSelectedText()
670 689 intercepted = True
671 690
691 elif key == QtCore.Qt.Key_Greater:
692 self._control.moveCursor(QtGui.QTextCursor.End)
693 intercepted = True
694
695 elif key == QtCore.Qt.Key_Less:
696 self._control.setTextCursor(self._get_prompt_cursor())
697 intercepted = True
698
672 699 else:
673 700 if key in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter):
674 if self._reading:
675 self._append_plain_text('\n')
676 self._reading = False
677 if self._reading_callback:
678 self._reading_callback()
679 elif not self._executing:
680 self.execute(interactive=True)
681 701 intercepted = True
702 if self._in_buffer(position):
703 if self._reading:
704 self._append_plain_text('\n')
705 self._reading = False
706 if self._reading_callback:
707 self._reading_callback()
708
709 # If there is only whitespace after the cursor, execute.
710 # Otherwise, split the line with a continuation prompt.
711 elif not self._executing:
712 cursor.movePosition(QtGui.QTextCursor.End,
713 QtGui.QTextCursor.KeepAnchor)
714 if cursor.selectedText().trimmed().isEmpty():
715 self.execute(interactive=True)
716 else:
717 cursor.beginEditBlock()
718 cursor.setPosition(position)
719 cursor.insertText('\n')
720 self._insert_continuation_prompt(cursor)
721
722 # Ensure that the whole input buffer is visible.
723 # FIXME: This will not be usable if the input buffer
724 # is taller than the console widget.
725 self._control.moveCursor(QtGui.QTextCursor.End)
726 self._control.setTextCursor(cursor)
727
728 cursor.endEditBlock()
682 729
683 730 elif key == QtCore.Qt.Key_Up:
684 731 if self._reading or not self._up_pressed():
@@ -702,17 +749,20 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
702 749 intercepted = not self._in_buffer(position - 1)
703 750
704 751 elif key == QtCore.Qt.Key_Home:
705 cursor.movePosition(QtGui.QTextCursor.StartOfBlock)
706 752 start_line = cursor.blockNumber()
707 753 if start_line == self._get_prompt_cursor().blockNumber():
708 754 start_pos = self._prompt_pos
709 755 else:
756 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
757 QtGui.QTextCursor.KeepAnchor)
710 758 start_pos = cursor.position()
711 759 start_pos += len(self._continuation_prompt)
760 cursor.setPosition(position)
712 761 if shift_down and self._in_buffer(position):
713 self._set_selection(position, start_pos)
762 cursor.setPosition(start_pos, QtGui.QTextCursor.KeepAnchor)
714 763 else:
715 self._set_position(start_pos)
764 cursor.setPosition(start_pos)
765 self._set_cursor(cursor)
716 766 intercepted = True
717 767
718 768 elif key == QtCore.Qt.Key_Backspace:
@@ -769,8 +819,24 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
769 819 interface.
770 820 """
771 821 key = event.key()
822 ctrl_down = self._control_key_down(event.modifiers())
823 alt_down = event.modifiers() & QtCore.Qt.AltModifier
824
825 if ctrl_down:
826 if key == QtCore.Qt.Key_O:
827 self._control.setFocus()
828 intercept = True
829
830 elif alt_down:
831 if key == QtCore.Qt.Key_Greater:
832 self._page_control.moveCursor(QtGui.QTextCursor.End)
833 intercepted = True
834
835 elif key == QtCore.Qt.Key_Less:
836 self._page_control.moveCursor(QtGui.QTextCursor.Start)
837 intercepted = True
772 838
773 if key in (QtCore.Qt.Key_Q, QtCore.Qt.Key_Escape):
839 elif key in (QtCore.Qt.Key_Q, QtCore.Qt.Key_Escape):
774 840 if self._splitter:
775 841 self._page_control.hide()
776 842 else:
@@ -779,7 +845,14 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
779 845
780 846 elif key in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return):
781 847 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
782 QtCore.Qt.Key_Down,
848 QtCore.Qt.Key_PageDown,
849 QtCore.Qt.NoModifier)
850 QtGui.qApp.sendEvent(self._page_control, new_event)
851 return True
852
853 elif key == QtCore.Qt.Key_Backspace:
854 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
855 QtCore.Qt.Key_PageUp,
783 856 QtCore.Qt.NoModifier)
784 857 QtGui.qApp.sendEvent(self._page_control, new_event)
785 858 return True
@@ -964,6 +1037,15 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
964 1037 cursor.setPosition(position)
965 1038 return cursor
966 1039
1040 def _insert_continuation_prompt(self, cursor):
1041 """ Inserts new continuation prompt using the specified cursor.
1042 """
1043 if self._continuation_prompt_html is None:
1044 self._insert_plain_text(cursor, self._continuation_prompt)
1045 else:
1046 self._continuation_prompt = self._insert_html_fetching_plain_text(
1047 cursor, self._continuation_prompt_html)
1048
967 1049 def _insert_html(self, cursor, html):
968 1050 """ Inserts HTML using the specified cursor in such a way that future
969 1051 formatting is unaffected.
@@ -1184,18 +1266,6 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
1184 1266 """
1185 1267 self._control.setTextCursor(cursor)
1186 1268
1187 def _set_position(self, position):
1188 """ Convenience method to set the position of the cursor.
1189 """
1190 cursor = self._control.textCursor()
1191 cursor.setPosition(position)
1192 self._control.setTextCursor(cursor)
1193
1194 def _set_selection(self, start, end):
1195 """ Convenience method to set the current selected text.
1196 """
1197 self._control.setTextCursor(self._get_selection_cursor(start, end))
1198
1199 1269 def _show_context_menu(self, pos):
1200 1270 """ Shows a context menu at the given QPoint (in widget coordinates).
1201 1271 """
@@ -1259,15 +1329,6 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
1259 1329 self._prompt_pos = self._get_end_cursor().position()
1260 1330 self._prompt_started()
1261 1331
1262 def _show_continuation_prompt(self):
1263 """ Writes a new continuation prompt at the end of the buffer.
1264 """
1265 if self._continuation_prompt_html is None:
1266 self._append_plain_text(self._continuation_prompt)
1267 else:
1268 self._continuation_prompt = self._append_html_fetching_plain_text(
1269 self._continuation_prompt_html)
1270
1271 1332
1272 1333 class HistoryConsoleWidget(ConsoleWidget):
1273 1334 """ A ConsoleWidget that keeps a history of the commands that have been
@@ -9,7 +9,8 b' from PyQt4 import QtCore, QtGui'
9 9 # Local imports
10 10 from IPython.core.inputsplitter import InputSplitter
11 11 from IPython.frontend.qt.base_frontend_mixin import BaseFrontendMixin
12 from IPython.utils.traitlets import Bool, Type
12 from IPython.utils.traitlets import Bool
13 from bracket_matcher import BracketMatcher
13 14 from call_tip_widget import CallTipWidget
14 15 from completion_lexer import CompletionLexer
15 16 from console_widget import HistoryConsoleWidget
@@ -88,8 +89,7 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
88 89 executed = QtCore.pyqtSignal(object)
89 90
90 91 # Protected class variables.
91 _highlighter_class = Type(FrontendHighlighter)
92 _input_splitter_class = Type(InputSplitter)
92 _input_splitter_class = InputSplitter
93 93
94 94 #---------------------------------------------------------------------------
95 95 # 'object' interface
@@ -99,10 +99,11 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
99 99 super(FrontendWidget, self).__init__(*args, **kw)
100 100
101 101 # FrontendWidget protected variables.
102 self._bracket_matcher = BracketMatcher(self._control)
102 103 self._call_tip_widget = CallTipWidget(self._control)
103 104 self._completion_lexer = CompletionLexer(PythonLexer())
104 105 self._hidden = False
105 self._highlighter = self._highlighter_class(self)
106 self._highlighter = FrontendHighlighter(self)
106 107 self._input_splitter = self._input_splitter_class(input_mode='block')
107 108 self._kernel_manager = None
108 109
@@ -170,8 +171,8 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
170 171 """ Reimplemented to allow execution interruption.
171 172 """
172 173 key = event.key()
173 if self._executing and self._control_key_down(event.modifiers()):
174 if key == QtCore.Qt.Key_C:
174 if self._control_key_down(event.modifiers()):
175 if key == QtCore.Qt.Key_C and self._executing:
175 176 self._kernel_interrupt()
176 177 return True
177 178 elif key == QtCore.Qt.Key_Period:
@@ -179,13 +180,13 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
179 180 return True
180 181 return super(FrontendWidget, self)._event_filter_console_keypress(event)
181 182
182 def _show_continuation_prompt(self):
183 def _insert_continuation_prompt(self, cursor):
183 184 """ Reimplemented for auto-indentation.
184 185 """
185 super(FrontendWidget, self)._show_continuation_prompt()
186 super(FrontendWidget, self)._insert_continuation_prompt(cursor)
186 187 spaces = self._input_splitter.indent_spaces
187 self._append_plain_text('\t' * (spaces / self.tab_width))
188 self._append_plain_text(' ' * (spaces % self.tab_width))
188 cursor.insertText('\t' * (spaces / self.tab_width))
189 cursor.insertText(' ' * (spaces % self.tab_width))
189 190
190 191 #---------------------------------------------------------------------------
191 192 # 'BaseFrontendMixin' abstract interface
@@ -353,15 +354,20 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
353 354 if self.custom_restart:
354 355 self.custom_restart_requested.emit()
355 356 elif self.kernel_manager.has_kernel:
356 try:
357 self.kernel_manager.restart_kernel()
358 except RuntimeError:
359 message = 'Kernel started externally. Cannot restart.\n'
360 self._append_plain_text(message)
361 else:
362 self._stopped_channels()
363 self._append_plain_text('Kernel restarting...\n')
364 self._show_interpreter_prompt()
357 message = 'Are you sure you want to restart the kernel?'
358 buttons = QtGui.QMessageBox.Yes | QtGui.QMessageBox.No
359 result = QtGui.QMessageBox.question(self, 'Restart kernel?',
360 message, buttons)
361 if result == QtGui.QMessageBox.Yes:
362 try:
363 self.kernel_manager.restart_kernel()
364 except RuntimeError:
365 message = 'Kernel started externally. Cannot restart.\n'
366 self._append_plain_text(message)
367 else:
368 self._stopped_channels()
369 self._append_plain_text('Kernel restarting...\n')
370 self._show_interpreter_prompt()
365 371 else:
366 372 self._append_plain_text('Kernel process is either remote or '
367 373 'unspecified. Cannot restart.\n')
@@ -110,14 +110,12 b' class PygmentsHighlighter(QtGui.QSyntaxHighlighter):'
110 110 elif hasattr(self._lexer, '_saved_state_stack'):
111 111 del self._lexer._saved_state_stack
112 112
113 index = 0
114 113 # Lex the text using Pygments
114 index = 0
115 115 for token, text in self._lexer.get_tokens(qstring):
116 l = len(text)
117 format = self._get_format(token)
118 if format is not None:
119 self.setFormat(index, l, format)
120 index += l
116 length = len(text)
117 self.setFormat(index, length, self._get_format(token))
118 index += length
121 119
122 120 if hasattr(self._lexer, '_saved_state_stack'):
123 121 data = PygmentsBlockUserData(
@@ -185,11 +183,9 b' class PygmentsHighlighter(QtGui.QSyntaxHighlighter):'
185 183 def _get_format_from_style(self, token, style):
186 184 """ Returns a QTextCharFormat for token by reading a Pygments style.
187 185 """
188 result = None
186 result = QtGui.QTextCharFormat()
189 187 for key, value in style.style_for_token(token).items():
190 188 if value:
191 if result is None:
192 result = QtGui.QTextCharFormat()
193 189 if key == 'color':
194 190 result.setForeground(self._get_brush(value))
195 191 elif key == 'bgcolor':
@@ -64,10 +64,8 b' def make_kernel(namespace, kernel_factory,'
64 64 """ Creates a kernel.
65 65 """
66 66 # Install minimal exception handling
67 color_scheme = 'LightBG' if sys.platform == 'darwin' else 'Linux'
68 sys.excepthook = FormattedTB(
69 mode='Verbose', color_scheme=color_scheme, ostream=sys.__stdout__
70 )
67 sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor',
68 ostream=sys.__stdout__)
71 69
72 70 # Create a context, a session, and the kernel sockets.
73 71 io.raw_print("Starting the kernel...")
@@ -101,4 +101,4 b' gitwash-update:'
101 101 cd source/development/gitwash && rename 's/.rst/.txt/' *.rst
102 102
103 103 nightly: dist
104 rsync -avH --delete dist/ ipython:www/doc/nightly No newline at end of file
104 rsync -avH --delete dist/ ipython:www/doc/nightly
@@ -130,20 +130,9 b' Messages on the XREP/XREQ socket'
130 130 Execute
131 131 -------
132 132
133 The execution request contains a single string, but this may be a multiline
134 string. The kernel is responsible for splitting this into possibly more than
135 one block and deciding whether to compile these in 'single' or 'exec' mode.
136 We're still sorting out this policy. The current inputsplitter is capable of
137 splitting the input for blocks that can all be run as 'single', but in the long
138 run it may prove cleaner to only use 'single' mode for truly single-line
139 inputs, and run all multiline input in 'exec' mode. This would preserve the
140 natural behavior of single-line inputs while allowing long cells to behave more
141 likea a script. This design will be refined as we complete the implementation.
142
143 .. Note::
144
145 What today we call 'prompt requests' will be encoded in the
146 ``state_template`` field.
133 This message type is used by frontends to ask the kernel to execute code on
134 behalf of the user, in a namespace reserved to the user's variables (and thus
135 separate from the kernel's own internal code and variables).
147 136
148 137 Message type: ``execute_request``::
149 138
@@ -162,25 +151,75 b' Message type: ``execute_request``::'
162 151 # The default is False.
163 152 'silent' : bool,
164 153
165 # An optional string to request arbitrary state information from the
166 # kernel. This string is evaluated via the itpl module, and it can
167 # therefore contain arbitrary code for execution.
168
169 'state_template' : str,
154 # A list of variable names from the user's namespace to be retrieved. What
155 # returns is a JSON string of the variable's repr(), not a python object.
156 'user_variables' : list,
157
158 # Similarly, a dict mapping names to expressions to be evaluated in the
159 # user's dict.
160 'user_expressions' : dict,
170 161 }
171 162
163 The ``code`` field contains a single string, but this may be a multiline
164 string. The kernel is responsible for splitting this into possibly more than
165 one block and deciding whether to compile these in 'single' or 'exec' mode.
166 We're still sorting out this policy. The current inputsplitter is capable of
167 splitting the input for blocks that can all be run as 'single', but in the long
168 run it may prove cleaner to only use 'single' mode for truly single-line
169 inputs, and run all multiline input in 'exec' mode. This would preserve the
170 natural behavior of single-line inputs while allowing long cells to behave more
171 likea a script. This design will be refined as we complete the implementation.
172
173 The ``user_`` fields deserve a detailed explanation. In the past, IPython had
174 the notion of a prompt string that allowed arbitrary code to be evaluated, and
175 this was put to good use by many in creating prompts that displayed system
176 status, path information, and even more esoteric uses like remote instrument
177 status aqcuired over the network. But now that IPython has a clean separation
178 between the kernel and the clients, the notion of embedding 'prompt'
179 maninpulations into the kernel itself feels awkward. Prompts should be a
180 frontend-side feature, and it should be even possible for different frontends
181 to display different prompts while interacting with the same kernel.
182
183 We have therefore abandoned the idea of a 'prompt string' to be evaluated by
184 the kernel, and instead provide the ability to retrieve from the user's
185 namespace information after the execution of the main ``code``, with two fields
186 of the execution request:
187
188 - ``user_variables``: If only variables from the user's namespace are needed, a
189 list of variable names can be passed and a dict with these names as keys and
190 their :func:`repr()` as values will be returned.
191
192 - ``user_expressions``: For more complex expressions that require function
193 evaluations, a dict can be provided with string keys and arbitrary python
194 expressions as values. The return message will contain also a dict with the
195 same keys and the :func:`repr()` of the evaluated expressions as value.
196
197 With this information, frontends can display any status information they wish
198 in the form that best suits each frontend (a status line, a popup, inline for a
199 terminal, etc).
200
201 .. Note::
202
203 In order to obtain the current execution counter for the purposes of
204 displaying input prompts, frontends simply make an execution request with an
205 empty code string and ``silent=True``.
206
172 207 Execution semantics
173 Upon execution of the ``code`` field, the kernel *always* sends a reply,
174 with a status code indicating what happened and additional data depending
175 on the outcome.
208 Upon completion of the execution request, the kernel *always* sends a
209 reply, with a status code indicating what happened and additional data
210 depending on the outcome.
211
212 The ``code`` field is executed first, and then the ``user_variables`` and
213 ``user_expressions`` are computed. This ensures that any error in the
214 latter don't harm the main code execution.
176 215
177 Any code in the ``state_template`` string is evaluated, but full exceptions
178 that may occur are *not* propagated back. If any error occurs during the
179 evaluation, the value of the string will simply be::
216 Any error in retrieving the ``user_variables`` or evaluating the
217 ``user_expressions`` will result in a simple error message in the return
218 fields of the form::
180 219
181 [ERROR in <contents of template>: ExceptionType - Exception message]
220 [ERROR] ExceptionType: Exception message
182 221
183 The user can simply send the same code contained in the template for normal
222 The user can simply send the same variable name or expression for
184 223 evaluation to see a regular traceback.
185 224
186 225 Execution counter (old prompt number)
@@ -280,7 +319,7 b' Message type: ``getattr_request``::'
280 319
281 320 content = {
282 321 # The (possibly dotted) name of the attribute
283 'name' : str
322 'name' : str,
284 323 }
285 324
286 325 When a ``getattr_request`` fails, there are two possible error types:
@@ -296,20 +335,20 b' Message type: ``getattr_reply``::'
296 335
297 336 content = {
298 337 # One of ['ok', 'AttributeError', 'AccessError'].
299 'status' : str
338 'status' : str,
300 339 # If status is 'ok', a JSON object.
301 'value' : object
340 'value' : object,
302 341 }
303 342
304 343 Message type: ``setattr_request``::
305 344
306 345 content = {
307 346 # The (possibly dotted) name of the attribute
308 'name' : str
347 'name' : str,
309 348
310 349 # A JSON-encoded object, that will be validated by the Traits
311 350 # information in the kernel
312 'value' : object
351 'value' : object,
313 352 }
314 353
315 354 When a ``setattr_request`` fails, there are also two possible error types with
@@ -319,7 +358,7 b' Message type: ``setattr_reply``::'
319 358
320 359 content = {
321 360 # One of ['ok', 'AttributeError', 'AccessError'].
322 'status' : str
361 'status' : str,
323 362 }
324 363
325 364
@@ -391,13 +430,13 b' Message type: ``object_info_reply``::'
391 430 # The name of the varargs (*args), if any
392 431 varargs : str,
393 432 # The name of the varkw (**kw), if any
394 varkw : str
433 varkw : str,
395 434 # The values (as strings) of all default arguments. Note
396 435 # that these must be matched *in reverse* with the 'args'
397 436 # list above, since the first positional args have no default
398 437 # value at all.
399 func_defaults : list
400 }
438 func_defaults : list,
439 },
401 440
402 441 # For instances, provide the constructor signature (the definition of
403 442 # the __init__ method):
@@ -487,6 +526,7 b' Message type: ``history_reply``::'
487 526 # respectively.
488 527 'history' : dict,
489 528 }
529
490 530 Messages on the PUB/SUB socket
491 531 ==============================
492 532
@@ -539,10 +579,10 b' Message type: ``pyout``::'
539 579 # The data is typically the repr() of the object.
540 580 'data' : str,
541 581
542 # The prompt number for this execution is also provided so that clients
543 # can display it, since IPython automatically creates variables called
544 # _N (for prompt N).
545 'prompt_number' : int,
582 # The counter for this execution is also provided so that clients can
583 # display it, since IPython automatically creates variables called _N (for
584 # prompt N).
585 'execution_count' : int,
546 586 }
547 587
548 588 Python errors
General Comments 0
You need to be logged in to leave comments. Login now