##// END OF EJS Templates
color settings from ipythonqt propagate down to the ZMQInteractiveShell in the Kernel
MinRK -
Show More
@@ -1,460 +1,463 b''
1 """ A FrontendWidget that emulates the interface of the console IPython and
1 """ A FrontendWidget that emulates the interface of the console IPython and
2 supports the additional functionality provided by the IPython kernel.
2 supports the additional functionality provided by the IPython kernel.
3
3
4 TODO: Add support for retrieving the system default editor. Requires code
4 TODO: Add support for retrieving the system default editor. Requires code
5 paths for Windows (use the registry), Mac OS (use LaunchServices), and
5 paths for Windows (use the registry), Mac OS (use LaunchServices), and
6 Linux (use the xdg system).
6 Linux (use the xdg system).
7 """
7 """
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Imports
10 # Imports
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 # Standard library imports
13 # Standard library imports
14 from collections import namedtuple
14 from collections import namedtuple
15 import re
15 import re
16 from subprocess import Popen
16 from subprocess import Popen
17 from textwrap import dedent
17 from textwrap import dedent
18
18
19 # System library imports
19 # System library imports
20 from PyQt4 import QtCore, QtGui
20 from PyQt4 import QtCore, QtGui
21
21
22 # Local imports
22 # Local imports
23 from IPython.core.inputsplitter import IPythonInputSplitter, \
23 from IPython.core.inputsplitter import IPythonInputSplitter, \
24 transform_ipy_prompt
24 transform_ipy_prompt
25 from IPython.core.usage import default_gui_banner
25 from IPython.core.usage import default_gui_banner
26 from IPython.utils.traitlets import Bool, Str
26 from IPython.utils.traitlets import Bool, Str
27 from frontend_widget import FrontendWidget
27 from frontend_widget import FrontendWidget
28 from styles import (default_light_style_sheet, default_light_syntax_style,
28 from styles import (default_light_style_sheet, default_light_syntax_style,
29 default_dark_style_sheet, default_dark_syntax_style,
29 default_dark_style_sheet, default_dark_syntax_style,
30 default_bw_style_sheet, default_bw_syntax_style)
30 default_bw_style_sheet, default_bw_syntax_style)
31
31
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33 # Constants
33 # Constants
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35
35
36 # Default strings to build and display input and output prompts (and separators
36 # Default strings to build and display input and output prompts (and separators
37 # in between)
37 # in between)
38 default_in_prompt = 'In [<span class="in-prompt-number">%i</span>]: '
38 default_in_prompt = 'In [<span class="in-prompt-number">%i</span>]: '
39 default_out_prompt = 'Out[<span class="out-prompt-number">%i</span>]: '
39 default_out_prompt = 'Out[<span class="out-prompt-number">%i</span>]: '
40 default_input_sep = '\n'
40 default_input_sep = '\n'
41 default_output_sep = ''
41 default_output_sep = ''
42 default_output_sep2 = ''
42 default_output_sep2 = ''
43
43
44 # Base path for most payload sources.
44 # Base path for most payload sources.
45 zmq_shell_source = 'IPython.zmq.zmqshell.ZMQInteractiveShell'
45 zmq_shell_source = 'IPython.zmq.zmqshell.ZMQInteractiveShell'
46
46
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48 # IPythonWidget class
48 # IPythonWidget class
49 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
50
50
51 class IPythonWidget(FrontendWidget):
51 class IPythonWidget(FrontendWidget):
52 """ A FrontendWidget for an IPython kernel.
52 """ A FrontendWidget for an IPython kernel.
53 """
53 """
54
54
55 # If set, the 'custom_edit_requested(str, int)' signal will be emitted when
55 # If set, the 'custom_edit_requested(str, int)' signal will be emitted when
56 # an editor is needed for a file. This overrides 'editor' and 'editor_line'
56 # an editor is needed for a file. This overrides 'editor' and 'editor_line'
57 # settings.
57 # settings.
58 custom_edit = Bool(False)
58 custom_edit = Bool(False)
59 custom_edit_requested = QtCore.pyqtSignal(object, object)
59 custom_edit_requested = QtCore.pyqtSignal(object, object)
60
60
61 # A command for invoking a system text editor. If the string contains a
61 # A command for invoking a system text editor. If the string contains a
62 # {filename} format specifier, it will be used. Otherwise, the filename will
62 # {filename} format specifier, it will be used. Otherwise, the filename will
63 # be appended to the end the command.
63 # be appended to the end the command.
64 editor = Str('default', config=True)
64 editor = Str('default', config=True)
65
65
66 # The editor command to use when a specific line number is requested. The
66 # The editor command to use when a specific line number is requested. The
67 # string should contain two format specifiers: {line} and {filename}. If
67 # string should contain two format specifiers: {line} and {filename}. If
68 # this parameter is not specified, the line number option to the %edit magic
68 # this parameter is not specified, the line number option to the %edit magic
69 # will be ignored.
69 # will be ignored.
70 editor_line = Str(config=True)
70 editor_line = Str(config=True)
71
71
72 # A CSS stylesheet. The stylesheet can contain classes for:
72 # A CSS stylesheet. The stylesheet can contain classes for:
73 # 1. Qt: QPlainTextEdit, QFrame, QWidget, etc
73 # 1. Qt: QPlainTextEdit, QFrame, QWidget, etc
74 # 2. Pygments: .c, .k, .o, etc (see PygmentsHighlighter)
74 # 2. Pygments: .c, .k, .o, etc (see PygmentsHighlighter)
75 # 3. IPython: .error, .in-prompt, .out-prompt, etc
75 # 3. IPython: .error, .in-prompt, .out-prompt, etc
76 style_sheet = Str(config=True)
76 style_sheet = Str(config=True)
77
77
78 # If not empty, use this Pygments style for syntax highlighting. Otherwise,
78 # If not empty, use this Pygments style for syntax highlighting. Otherwise,
79 # the style sheet is queried for Pygments style information.
79 # the style sheet is queried for Pygments style information.
80 syntax_style = Str(config=True)
80 syntax_style = Str(config=True)
81
81
82 # Prompts.
82 # Prompts.
83 in_prompt = Str(default_in_prompt, config=True)
83 in_prompt = Str(default_in_prompt, config=True)
84 out_prompt = Str(default_out_prompt, config=True)
84 out_prompt = Str(default_out_prompt, config=True)
85 input_sep = Str(default_input_sep, config=True)
85 input_sep = Str(default_input_sep, config=True)
86 output_sep = Str(default_output_sep, config=True)
86 output_sep = Str(default_output_sep, config=True)
87 output_sep2 = Str(default_output_sep2, config=True)
87 output_sep2 = Str(default_output_sep2, config=True)
88
88
89 # FrontendWidget protected class variables.
89 # FrontendWidget protected class variables.
90 _input_splitter_class = IPythonInputSplitter
90 _input_splitter_class = IPythonInputSplitter
91
91
92 # IPythonWidget protected class variables.
92 # IPythonWidget protected class variables.
93 _PromptBlock = namedtuple('_PromptBlock', ['block', 'length', 'number'])
93 _PromptBlock = namedtuple('_PromptBlock', ['block', 'length', 'number'])
94 _payload_source_edit = zmq_shell_source + '.edit_magic'
94 _payload_source_edit = zmq_shell_source + '.edit_magic'
95 _payload_source_exit = zmq_shell_source + '.ask_exit'
95 _payload_source_exit = zmq_shell_source + '.ask_exit'
96 _payload_source_loadpy = zmq_shell_source + '.magic_loadpy'
96 _payload_source_loadpy = zmq_shell_source + '.magic_loadpy'
97 _payload_source_page = 'IPython.zmq.page.page'
97 _payload_source_page = 'IPython.zmq.page.page'
98
98
99 #---------------------------------------------------------------------------
99 #---------------------------------------------------------------------------
100 # 'object' interface
100 # 'object' interface
101 #---------------------------------------------------------------------------
101 #---------------------------------------------------------------------------
102
102
103 def __init__(self, *args, **kw):
103 def __init__(self, *args, **kw):
104 super(IPythonWidget, self).__init__(*args, **kw)
104 super(IPythonWidget, self).__init__(*args, **kw)
105
105
106 # IPythonWidget protected variables.
106 # IPythonWidget protected variables.
107 self._code_to_load = None
107 self._code_to_load = None
108 self._payload_handlers = {
108 self._payload_handlers = {
109 self._payload_source_edit : self._handle_payload_edit,
109 self._payload_source_edit : self._handle_payload_edit,
110 self._payload_source_exit : self._handle_payload_exit,
110 self._payload_source_exit : self._handle_payload_exit,
111 self._payload_source_page : self._handle_payload_page,
111 self._payload_source_page : self._handle_payload_page,
112 self._payload_source_loadpy : self._handle_payload_loadpy }
112 self._payload_source_loadpy : self._handle_payload_loadpy }
113 self._previous_prompt_obj = None
113 self._previous_prompt_obj = None
114
114
115 # Initialize widget styling.
115 # Initialize widget styling.
116 if self.style_sheet:
116 if self.style_sheet:
117 self._style_sheet_changed()
117 self._style_sheet_changed()
118 self._syntax_style_changed()
118 self._syntax_style_changed()
119 else:
119 else:
120 self.set_default_style()
120 self.set_default_style()
121
121
122 #---------------------------------------------------------------------------
122 #---------------------------------------------------------------------------
123 # 'BaseFrontendMixin' abstract interface
123 # 'BaseFrontendMixin' abstract interface
124 #---------------------------------------------------------------------------
124 #---------------------------------------------------------------------------
125
125
126 def _handle_complete_reply(self, rep):
126 def _handle_complete_reply(self, rep):
127 """ Reimplemented to support IPython's improved completion machinery.
127 """ Reimplemented to support IPython's improved completion machinery.
128 """
128 """
129 cursor = self._get_cursor()
129 cursor = self._get_cursor()
130 info = self._request_info.get('complete')
130 info = self._request_info.get('complete')
131 if info and info.id == rep['parent_header']['msg_id'] and \
131 if info and info.id == rep['parent_header']['msg_id'] and \
132 info.pos == cursor.position():
132 info.pos == cursor.position():
133 matches = rep['content']['matches']
133 matches = rep['content']['matches']
134 text = rep['content']['matched_text']
134 text = rep['content']['matched_text']
135 offset = len(text)
135 offset = len(text)
136
136
137 # Clean up matches with period and path separators if the matched
137 # Clean up matches with period and path separators if the matched
138 # text has not been transformed. This is done by truncating all
138 # text has not been transformed. This is done by truncating all
139 # but the last component and then suitably decreasing the offset
139 # but the last component and then suitably decreasing the offset
140 # between the current cursor position and the start of completion.
140 # between the current cursor position and the start of completion.
141 if len(matches) > 1 and matches[0][:offset] == text:
141 if len(matches) > 1 and matches[0][:offset] == text:
142 parts = re.split(r'[./\\]', text)
142 parts = re.split(r'[./\\]', text)
143 sep_count = len(parts) - 1
143 sep_count = len(parts) - 1
144 if sep_count:
144 if sep_count:
145 chop_length = sum(map(len, parts[:sep_count])) + sep_count
145 chop_length = sum(map(len, parts[:sep_count])) + sep_count
146 matches = [ match[chop_length:] for match in matches ]
146 matches = [ match[chop_length:] for match in matches ]
147 offset -= chop_length
147 offset -= chop_length
148
148
149 # Move the cursor to the start of the match and complete.
149 # Move the cursor to the start of the match and complete.
150 cursor.movePosition(QtGui.QTextCursor.Left, n=offset)
150 cursor.movePosition(QtGui.QTextCursor.Left, n=offset)
151 self._complete_with_items(cursor, matches)
151 self._complete_with_items(cursor, matches)
152
152
153 def _handle_execute_reply(self, msg):
153 def _handle_execute_reply(self, msg):
154 """ Reimplemented to support prompt requests.
154 """ Reimplemented to support prompt requests.
155 """
155 """
156 info = self._request_info.get('execute')
156 info = self._request_info.get('execute')
157 if info and info.id == msg['parent_header']['msg_id']:
157 if info and info.id == msg['parent_header']['msg_id']:
158 if info.kind == 'prompt':
158 if info.kind == 'prompt':
159 number = msg['content']['execution_count'] + 1
159 number = msg['content']['execution_count'] + 1
160 self._show_interpreter_prompt(number)
160 self._show_interpreter_prompt(number)
161 else:
161 else:
162 super(IPythonWidget, self)._handle_execute_reply(msg)
162 super(IPythonWidget, self)._handle_execute_reply(msg)
163
163
164 def _handle_history_reply(self, msg):
164 def _handle_history_reply(self, msg):
165 """ Implemented to handle history replies, which are only supported by
165 """ Implemented to handle history replies, which are only supported by
166 the IPython kernel.
166 the IPython kernel.
167 """
167 """
168 history_dict = msg['content']['history']
168 history_dict = msg['content']['history']
169 items = [ history_dict[key] for key in sorted(history_dict.keys()) ]
169 items = [ history_dict[key] for key in sorted(history_dict.keys()) ]
170 self._set_history(items)
170 self._set_history(items)
171
171
172 def _handle_pyout(self, msg):
172 def _handle_pyout(self, msg):
173 """ Reimplemented for IPython-style "display hook".
173 """ Reimplemented for IPython-style "display hook".
174 """
174 """
175 if not self._hidden and self._is_from_this_session(msg):
175 if not self._hidden and self._is_from_this_session(msg):
176 content = msg['content']
176 content = msg['content']
177 prompt_number = content['execution_count']
177 prompt_number = content['execution_count']
178 self._append_plain_text(self.output_sep)
178 self._append_plain_text(self.output_sep)
179 self._append_html(self._make_out_prompt(prompt_number))
179 self._append_html(self._make_out_prompt(prompt_number))
180 self._append_plain_text(content['data']+self.output_sep2)
180 self._append_plain_text(content['data']+self.output_sep2)
181
181
182 def _started_channels(self):
182 def _started_channels(self):
183 """ Reimplemented to make a history request.
183 """ Reimplemented to make a history request.
184 """
184 """
185 super(IPythonWidget, self)._started_channels()
185 super(IPythonWidget, self)._started_channels()
186 # FIXME: Disabled until history requests are properly implemented.
186 # FIXME: Disabled until history requests are properly implemented.
187 #self.kernel_manager.xreq_channel.history(raw=True, output=False)
187 #self.kernel_manager.xreq_channel.history(raw=True, output=False)
188
188
189 #---------------------------------------------------------------------------
189 #---------------------------------------------------------------------------
190 # 'ConsoleWidget' public interface
190 # 'ConsoleWidget' public interface
191 #---------------------------------------------------------------------------
191 #---------------------------------------------------------------------------
192
192
193 def copy(self):
193 def copy(self):
194 """ Copy the currently selected text to the clipboard, removing prompts
194 """ Copy the currently selected text to the clipboard, removing prompts
195 if possible.
195 if possible.
196 """
196 """
197 text = unicode(self._control.textCursor().selection().toPlainText())
197 text = unicode(self._control.textCursor().selection().toPlainText())
198 if text:
198 if text:
199 lines = map(transform_ipy_prompt, text.splitlines())
199 lines = map(transform_ipy_prompt, text.splitlines())
200 text = '\n'.join(lines)
200 text = '\n'.join(lines)
201 QtGui.QApplication.clipboard().setText(text)
201 QtGui.QApplication.clipboard().setText(text)
202
202
203 #---------------------------------------------------------------------------
203 #---------------------------------------------------------------------------
204 # 'FrontendWidget' public interface
204 # 'FrontendWidget' public interface
205 #---------------------------------------------------------------------------
205 #---------------------------------------------------------------------------
206
206
207 def execute_file(self, path, hidden=False):
207 def execute_file(self, path, hidden=False):
208 """ Reimplemented to use the 'run' magic.
208 """ Reimplemented to use the 'run' magic.
209 """
209 """
210 self.execute('%%run %s' % path, hidden=hidden)
210 self.execute('%%run %s' % path, hidden=hidden)
211
211
212 #---------------------------------------------------------------------------
212 #---------------------------------------------------------------------------
213 # 'FrontendWidget' protected interface
213 # 'FrontendWidget' protected interface
214 #---------------------------------------------------------------------------
214 #---------------------------------------------------------------------------
215
215
216 def _complete(self):
216 def _complete(self):
217 """ Reimplemented to support IPython's improved completion machinery.
217 """ Reimplemented to support IPython's improved completion machinery.
218 """
218 """
219 # We let the kernel split the input line, so we *always* send an empty
219 # We let the kernel split the input line, so we *always* send an empty
220 # text field. Readline-based frontends do get a real text field which
220 # text field. Readline-based frontends do get a real text field which
221 # they can use.
221 # they can use.
222 text = ''
222 text = ''
223
223
224 # Send the completion request to the kernel
224 # Send the completion request to the kernel
225 msg_id = self.kernel_manager.xreq_channel.complete(
225 msg_id = self.kernel_manager.xreq_channel.complete(
226 text, # text
226 text, # text
227 self._get_input_buffer_cursor_line(), # line
227 self._get_input_buffer_cursor_line(), # line
228 self._get_input_buffer_cursor_column(), # cursor_pos
228 self._get_input_buffer_cursor_column(), # cursor_pos
229 self.input_buffer) # block
229 self.input_buffer) # block
230 pos = self._get_cursor().position()
230 pos = self._get_cursor().position()
231 info = self._CompletionRequest(msg_id, pos)
231 info = self._CompletionRequest(msg_id, pos)
232 self._request_info['complete'] = info
232 self._request_info['complete'] = info
233
233
234 def _get_banner(self):
234 def _get_banner(self):
235 """ Reimplemented to return IPython's default banner.
235 """ Reimplemented to return IPython's default banner.
236 """
236 """
237 return default_gui_banner
237 return default_gui_banner
238
238
239 def _process_execute_error(self, msg):
239 def _process_execute_error(self, msg):
240 """ Reimplemented for IPython-style traceback formatting.
240 """ Reimplemented for IPython-style traceback formatting.
241 """
241 """
242 content = msg['content']
242 content = msg['content']
243 traceback = '\n'.join(content['traceback']) + '\n'
243 traceback = '\n'.join(content['traceback']) + '\n'
244 if False:
244 if False:
245 # FIXME: For now, tracebacks come as plain text, so we can't use
245 # FIXME: For now, tracebacks come as plain text, so we can't use
246 # the html renderer yet. Once we refactor ultratb to produce
246 # the html renderer yet. Once we refactor ultratb to produce
247 # properly styled tracebacks, this branch should be the default
247 # properly styled tracebacks, this branch should be the default
248 traceback = traceback.replace(' ', '&nbsp;')
248 traceback = traceback.replace(' ', '&nbsp;')
249 traceback = traceback.replace('\n', '<br/>')
249 traceback = traceback.replace('\n', '<br/>')
250
250
251 ename = content['ename']
251 ename = content['ename']
252 ename_styled = '<span class="error">%s</span>' % ename
252 ename_styled = '<span class="error">%s</span>' % ename
253 traceback = traceback.replace(ename, ename_styled)
253 traceback = traceback.replace(ename, ename_styled)
254
254
255 self._append_html(traceback)
255 self._append_html(traceback)
256 else:
256 else:
257 # This is the fallback for now, using plain text with ansi escapes
257 # This is the fallback for now, using plain text with ansi escapes
258 self._append_plain_text(traceback)
258 self._append_plain_text(traceback)
259
259
260 def _process_execute_payload(self, item):
260 def _process_execute_payload(self, item):
261 """ Reimplemented to dispatch payloads to handler methods.
261 """ Reimplemented to dispatch payloads to handler methods.
262 """
262 """
263 handler = self._payload_handlers.get(item['source'])
263 handler = self._payload_handlers.get(item['source'])
264 if handler is None:
264 if handler is None:
265 # We have no handler for this type of payload, simply ignore it
265 # We have no handler for this type of payload, simply ignore it
266 return False
266 return False
267 else:
267 else:
268 handler(item)
268 handler(item)
269 return True
269 return True
270
270
271 def _show_interpreter_prompt(self, number=None):
271 def _show_interpreter_prompt(self, number=None):
272 """ Reimplemented for IPython-style prompts.
272 """ Reimplemented for IPython-style prompts.
273 """
273 """
274 # If a number was not specified, make a prompt number request.
274 # If a number was not specified, make a prompt number request.
275 if number is None:
275 if number is None:
276 msg_id = self.kernel_manager.xreq_channel.execute('', silent=True)
276 msg_id = self.kernel_manager.xreq_channel.execute('', silent=True)
277 info = self._ExecutionRequest(msg_id, 'prompt')
277 info = self._ExecutionRequest(msg_id, 'prompt')
278 self._request_info['execute'] = info
278 self._request_info['execute'] = info
279 return
279 return
280
280
281 # Show a new prompt and save information about it so that it can be
281 # Show a new prompt and save information about it so that it can be
282 # updated later if the prompt number turns out to be wrong.
282 # updated later if the prompt number turns out to be wrong.
283 self._prompt_sep = self.input_sep
283 self._prompt_sep = self.input_sep
284 self._show_prompt(self._make_in_prompt(number), html=True)
284 self._show_prompt(self._make_in_prompt(number), html=True)
285 block = self._control.document().lastBlock()
285 block = self._control.document().lastBlock()
286 length = len(self._prompt)
286 length = len(self._prompt)
287 self._previous_prompt_obj = self._PromptBlock(block, length, number)
287 self._previous_prompt_obj = self._PromptBlock(block, length, number)
288
288
289 # Update continuation prompt to reflect (possibly) new prompt length.
289 # Update continuation prompt to reflect (possibly) new prompt length.
290 self._set_continuation_prompt(
290 self._set_continuation_prompt(
291 self._make_continuation_prompt(self._prompt), html=True)
291 self._make_continuation_prompt(self._prompt), html=True)
292
292
293 # Load code from the %loadpy magic, if necessary.
293 # Load code from the %loadpy magic, if necessary.
294 if self._code_to_load is not None:
294 if self._code_to_load is not None:
295 self.input_buffer = dedent(unicode(self._code_to_load).rstrip())
295 self.input_buffer = dedent(unicode(self._code_to_load).rstrip())
296 self._code_to_load = None
296 self._code_to_load = None
297
297
298 def _show_interpreter_prompt_for_reply(self, msg):
298 def _show_interpreter_prompt_for_reply(self, msg):
299 """ Reimplemented for IPython-style prompts.
299 """ Reimplemented for IPython-style prompts.
300 """
300 """
301 # Update the old prompt number if necessary.
301 # Update the old prompt number if necessary.
302 content = msg['content']
302 content = msg['content']
303 previous_prompt_number = content['execution_count']
303 previous_prompt_number = content['execution_count']
304 if self._previous_prompt_obj and \
304 if self._previous_prompt_obj and \
305 self._previous_prompt_obj.number != previous_prompt_number:
305 self._previous_prompt_obj.number != previous_prompt_number:
306 block = self._previous_prompt_obj.block
306 block = self._previous_prompt_obj.block
307
307
308 # Make sure the prompt block has not been erased.
308 # Make sure the prompt block has not been erased.
309 if block.isValid() and not block.text().isEmpty():
309 if block.isValid() and not block.text().isEmpty():
310
310
311 # Remove the old prompt and insert a new prompt.
311 # Remove the old prompt and insert a new prompt.
312 cursor = QtGui.QTextCursor(block)
312 cursor = QtGui.QTextCursor(block)
313 cursor.movePosition(QtGui.QTextCursor.Right,
313 cursor.movePosition(QtGui.QTextCursor.Right,
314 QtGui.QTextCursor.KeepAnchor,
314 QtGui.QTextCursor.KeepAnchor,
315 self._previous_prompt_obj.length)
315 self._previous_prompt_obj.length)
316 prompt = self._make_in_prompt(previous_prompt_number)
316 prompt = self._make_in_prompt(previous_prompt_number)
317 self._prompt = self._insert_html_fetching_plain_text(
317 self._prompt = self._insert_html_fetching_plain_text(
318 cursor, prompt)
318 cursor, prompt)
319
319
320 # When the HTML is inserted, Qt blows away the syntax
320 # When the HTML is inserted, Qt blows away the syntax
321 # highlighting for the line, so we need to rehighlight it.
321 # highlighting for the line, so we need to rehighlight it.
322 self._highlighter.rehighlightBlock(cursor.block())
322 self._highlighter.rehighlightBlock(cursor.block())
323
323
324 self._previous_prompt_obj = None
324 self._previous_prompt_obj = None
325
325
326 # Show a new prompt with the kernel's estimated prompt number.
326 # Show a new prompt with the kernel's estimated prompt number.
327 self._show_interpreter_prompt(previous_prompt_number + 1)
327 self._show_interpreter_prompt(previous_prompt_number + 1)
328
328
329 #---------------------------------------------------------------------------
329 #---------------------------------------------------------------------------
330 # 'IPythonWidget' interface
330 # 'IPythonWidget' interface
331 #---------------------------------------------------------------------------
331 #---------------------------------------------------------------------------
332
332
333 def set_default_style(self, colors='light'):
333 def set_default_style(self, colors='lightbg'):
334 """ Sets the widget style to the class defaults.
334 """ Sets the widget style to the class defaults.
335
335
336 Parameters:
336 Parameters:
337 -----------
337 -----------
338 colors : str, optional (default light)
338 colors : str, optional (default lightbg)
339 Whether to use the default IPython light background or dark
339 Whether to use the default IPython light background or dark
340 background or B&W style.
340 background or B&W style.
341 """
341 """
342 if colors=='light':
342 colors = colors.lower()
343 if colors=='lightbg':
343 self.style_sheet = default_light_style_sheet
344 self.style_sheet = default_light_style_sheet
344 self.syntax_style = default_light_syntax_style
345 self.syntax_style = default_light_syntax_style
345 elif colors=='dark':
346 elif colors=='linux':
346 self.style_sheet = default_dark_style_sheet
347 self.style_sheet = default_dark_style_sheet
347 self.syntax_style = default_dark_syntax_style
348 self.syntax_style = default_dark_syntax_style
348 elif colors=='bw':
349 elif colors=='nocolor':
349 self.style_sheet = default_bw_style_sheet
350 self.style_sheet = default_bw_style_sheet
350 self.syntax_style = default_bw_syntax_style
351 self.syntax_style = default_bw_syntax_style
352 else:
353 raise KeyError("No such color scheme: %s"%colors)
351
354
352 #---------------------------------------------------------------------------
355 #---------------------------------------------------------------------------
353 # 'IPythonWidget' protected interface
356 # 'IPythonWidget' protected interface
354 #---------------------------------------------------------------------------
357 #---------------------------------------------------------------------------
355
358
356 def _edit(self, filename, line=None):
359 def _edit(self, filename, line=None):
357 """ Opens a Python script for editing.
360 """ Opens a Python script for editing.
358
361
359 Parameters:
362 Parameters:
360 -----------
363 -----------
361 filename : str
364 filename : str
362 A path to a local system file.
365 A path to a local system file.
363
366
364 line : int, optional
367 line : int, optional
365 A line of interest in the file.
368 A line of interest in the file.
366 """
369 """
367 if self.custom_edit:
370 if self.custom_edit:
368 self.custom_edit_requested.emit(filename, line)
371 self.custom_edit_requested.emit(filename, line)
369 elif self.editor == 'default':
372 elif self.editor == 'default':
370 self._append_plain_text('No default editor available.\n')
373 self._append_plain_text('No default editor available.\n')
371 else:
374 else:
372 try:
375 try:
373 filename = '"%s"' % filename
376 filename = '"%s"' % filename
374 if line and self.editor_line:
377 if line and self.editor_line:
375 command = self.editor_line.format(filename=filename,
378 command = self.editor_line.format(filename=filename,
376 line=line)
379 line=line)
377 else:
380 else:
378 try:
381 try:
379 command = self.editor.format()
382 command = self.editor.format()
380 except KeyError:
383 except KeyError:
381 command = self.editor.format(filename=filename)
384 command = self.editor.format(filename=filename)
382 else:
385 else:
383 command += ' ' + filename
386 command += ' ' + filename
384 except KeyError:
387 except KeyError:
385 self._append_plain_text('Invalid editor command.\n')
388 self._append_plain_text('Invalid editor command.\n')
386 else:
389 else:
387 try:
390 try:
388 Popen(command, shell=True)
391 Popen(command, shell=True)
389 except OSError:
392 except OSError:
390 msg = 'Opening editor with command "%s" failed.\n'
393 msg = 'Opening editor with command "%s" failed.\n'
391 self._append_plain_text(msg % command)
394 self._append_plain_text(msg % command)
392
395
393 def _make_in_prompt(self, number):
396 def _make_in_prompt(self, number):
394 """ Given a prompt number, returns an HTML In prompt.
397 """ Given a prompt number, returns an HTML In prompt.
395 """
398 """
396 body = self.in_prompt % number
399 body = self.in_prompt % number
397 return '<span class="in-prompt">%s</span>' % body
400 return '<span class="in-prompt">%s</span>' % body
398
401
399 def _make_continuation_prompt(self, prompt):
402 def _make_continuation_prompt(self, prompt):
400 """ Given a plain text version of an In prompt, returns an HTML
403 """ Given a plain text version of an In prompt, returns an HTML
401 continuation prompt.
404 continuation prompt.
402 """
405 """
403 end_chars = '...: '
406 end_chars = '...: '
404 space_count = len(prompt.lstrip('\n')) - len(end_chars)
407 space_count = len(prompt.lstrip('\n')) - len(end_chars)
405 body = '&nbsp;' * space_count + end_chars
408 body = '&nbsp;' * space_count + end_chars
406 return '<span class="in-prompt">%s</span>' % body
409 return '<span class="in-prompt">%s</span>' % body
407
410
408 def _make_out_prompt(self, number):
411 def _make_out_prompt(self, number):
409 """ Given a prompt number, returns an HTML Out prompt.
412 """ Given a prompt number, returns an HTML Out prompt.
410 """
413 """
411 body = self.out_prompt % number
414 body = self.out_prompt % number
412 return '<span class="out-prompt">%s</span>' % body
415 return '<span class="out-prompt">%s</span>' % body
413
416
414 #------ Payload handlers --------------------------------------------------
417 #------ Payload handlers --------------------------------------------------
415
418
416 # Payload handlers with a generic interface: each takes the opaque payload
419 # Payload handlers with a generic interface: each takes the opaque payload
417 # dict, unpacks it and calls the underlying functions with the necessary
420 # dict, unpacks it and calls the underlying functions with the necessary
418 # arguments.
421 # arguments.
419
422
420 def _handle_payload_edit(self, item):
423 def _handle_payload_edit(self, item):
421 self._edit(item['filename'], item['line_number'])
424 self._edit(item['filename'], item['line_number'])
422
425
423 def _handle_payload_exit(self, item):
426 def _handle_payload_exit(self, item):
424 self.exit_requested.emit()
427 self.exit_requested.emit()
425
428
426 def _handle_payload_loadpy(self, item):
429 def _handle_payload_loadpy(self, item):
427 # Simple save the text of the .py file for later. The text is written
430 # Simple save the text of the .py file for later. The text is written
428 # to the buffer when _prompt_started_hook is called.
431 # to the buffer when _prompt_started_hook is called.
429 self._code_to_load = item['text']
432 self._code_to_load = item['text']
430
433
431 def _handle_payload_page(self, item):
434 def _handle_payload_page(self, item):
432 # Since the plain text widget supports only a very small subset of HTML
435 # Since the plain text widget supports only a very small subset of HTML
433 # and we have no control over the HTML source, we only page HTML
436 # and we have no control over the HTML source, we only page HTML
434 # payloads in the rich text widget.
437 # payloads in the rich text widget.
435 if item['html'] and self.kind == 'rich':
438 if item['html'] and self.kind == 'rich':
436 self._page(item['html'], html=True)
439 self._page(item['html'], html=True)
437 else:
440 else:
438 self._page(item['text'], html=False)
441 self._page(item['text'], html=False)
439
442
440 #------ Trait change handlers ---------------------------------------------
443 #------ Trait change handlers ---------------------------------------------
441
444
442 def _style_sheet_changed(self):
445 def _style_sheet_changed(self):
443 """ Set the style sheets of the underlying widgets.
446 """ Set the style sheets of the underlying widgets.
444 """
447 """
445 self.setStyleSheet(self.style_sheet)
448 self.setStyleSheet(self.style_sheet)
446 self._control.document().setDefaultStyleSheet(self.style_sheet)
449 self._control.document().setDefaultStyleSheet(self.style_sheet)
447 if self._page_control:
450 if self._page_control:
448 self._page_control.document().setDefaultStyleSheet(self.style_sheet)
451 self._page_control.document().setDefaultStyleSheet(self.style_sheet)
449
452
450 bg_color = self._control.palette().background().color()
453 bg_color = self._control.palette().background().color()
451 self._ansi_processor.set_background_color(bg_color)
454 self._ansi_processor.set_background_color(bg_color)
452
455
453 def _syntax_style_changed(self):
456 def _syntax_style_changed(self):
454 """ Set the style for the syntax highlighter.
457 """ Set the style for the syntax highlighter.
455 """
458 """
456 if self.syntax_style:
459 if self.syntax_style:
457 self._highlighter.set_style(self.syntax_style)
460 self._highlighter.set_style(self.syntax_style)
458 else:
461 else:
459 self._highlighter.set_style_sheet(self.style_sheet)
462 self._highlighter.set_style_sheet(self.style_sheet)
460
463
@@ -1,247 +1,254 b''
1 """ A minimal application using the Qt console-style IPython frontend.
1 """ A minimal application using the Qt console-style IPython frontend.
2 """
2 """
3
3
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Imports
5 # Imports
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7
7
8 # Systemm library imports
8 # Systemm library imports
9 from PyQt4 import QtGui
9 from PyQt4 import QtGui
10 from pygments.styles import get_all_styles
10 from pygments.styles import get_all_styles
11 # Local imports
11 # Local imports
12 from IPython.external.argparse import ArgumentParser
12 from IPython.external.argparse import ArgumentParser
13 from IPython.frontend.qt.console.frontend_widget import FrontendWidget
13 from IPython.frontend.qt.console.frontend_widget import FrontendWidget
14 from IPython.frontend.qt.console.ipython_widget import IPythonWidget
14 from IPython.frontend.qt.console.ipython_widget import IPythonWidget
15 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
15 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
16 from IPython.frontend.qt.console import styles
16 from IPython.frontend.qt.console import styles
17 from IPython.frontend.qt.kernelmanager import QtKernelManager
17 from IPython.frontend.qt.kernelmanager import QtKernelManager
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Network Constants
20 # Network Constants
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22
22
23 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
23 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
24
24
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26 # Classes
26 # Classes
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28
28
29 class MainWindow(QtGui.QMainWindow):
29 class MainWindow(QtGui.QMainWindow):
30
30
31 #---------------------------------------------------------------------------
31 #---------------------------------------------------------------------------
32 # 'object' interface
32 # 'object' interface
33 #---------------------------------------------------------------------------
33 #---------------------------------------------------------------------------
34
34
35 def __init__(self, app, frontend, existing=False, may_close=True):
35 def __init__(self, app, frontend, existing=False, may_close=True):
36 """ Create a MainWindow for the specified FrontendWidget.
36 """ Create a MainWindow for the specified FrontendWidget.
37
37
38 The app is passed as an argument to allow for different
38 The app is passed as an argument to allow for different
39 closing behavior depending on whether we are the Kernel's parent.
39 closing behavior depending on whether we are the Kernel's parent.
40
40
41 If existing is True, then this Console does not own the Kernel.
41 If existing is True, then this Console does not own the Kernel.
42
42
43 If may_close is True, then this Console is permitted to close the kernel
43 If may_close is True, then this Console is permitted to close the kernel
44 """
44 """
45 super(MainWindow, self).__init__()
45 super(MainWindow, self).__init__()
46 self._app = app
46 self._app = app
47 self._frontend = frontend
47 self._frontend = frontend
48 self._existing = existing
48 self._existing = existing
49 if existing:
49 if existing:
50 self._may_close = may_close
50 self._may_close = may_close
51 else:
51 else:
52 self._may_close = True
52 self._may_close = True
53 self._frontend.exit_requested.connect(self.close)
53 self._frontend.exit_requested.connect(self.close)
54 self.setCentralWidget(frontend)
54 self.setCentralWidget(frontend)
55
55
56 #---------------------------------------------------------------------------
56 #---------------------------------------------------------------------------
57 # QWidget interface
57 # QWidget interface
58 #---------------------------------------------------------------------------
58 #---------------------------------------------------------------------------
59
59
60 def closeEvent(self, event):
60 def closeEvent(self, event):
61 """ Reimplemented to prompt the user and close the kernel cleanly.
61 """ Reimplemented to prompt the user and close the kernel cleanly.
62 """
62 """
63 kernel_manager = self._frontend.kernel_manager
63 kernel_manager = self._frontend.kernel_manager
64 if kernel_manager and kernel_manager.channels_running:
64 if kernel_manager and kernel_manager.channels_running:
65 title = self.window().windowTitle()
65 title = self.window().windowTitle()
66 cancel = QtGui.QMessageBox.Cancel
66 cancel = QtGui.QMessageBox.Cancel
67 okay = QtGui.QMessageBox.Ok
67 okay = QtGui.QMessageBox.Ok
68 if self._may_close:
68 if self._may_close:
69 msg = "You are closing this Console window."
69 msg = "You are closing this Console window."
70 info = "Would you like to quit the Kernel and all attached Consoles as well?"
70 info = "Would you like to quit the Kernel and all attached Consoles as well?"
71 justthis = QtGui.QPushButton("&No, just this Console", self)
71 justthis = QtGui.QPushButton("&No, just this Console", self)
72 justthis.setShortcut('N')
72 justthis.setShortcut('N')
73 closeall = QtGui.QPushButton("&Yes, quit everything", self)
73 closeall = QtGui.QPushButton("&Yes, quit everything", self)
74 closeall.setShortcut('Y')
74 closeall.setShortcut('Y')
75 box = QtGui.QMessageBox(QtGui.QMessageBox.Question, title, msg)
75 box = QtGui.QMessageBox(QtGui.QMessageBox.Question, title, msg)
76 box.setInformativeText(info)
76 box.setInformativeText(info)
77 box.addButton(cancel)
77 box.addButton(cancel)
78 box.addButton(justthis, QtGui.QMessageBox.NoRole)
78 box.addButton(justthis, QtGui.QMessageBox.NoRole)
79 box.addButton(closeall, QtGui.QMessageBox.YesRole)
79 box.addButton(closeall, QtGui.QMessageBox.YesRole)
80 box.setDefaultButton(closeall)
80 box.setDefaultButton(closeall)
81 box.setEscapeButton(cancel)
81 box.setEscapeButton(cancel)
82 reply = box.exec_()
82 reply = box.exec_()
83 if reply == 1: # close All
83 if reply == 1: # close All
84 kernel_manager.shutdown_kernel()
84 kernel_manager.shutdown_kernel()
85 #kernel_manager.stop_channels()
85 #kernel_manager.stop_channels()
86 event.accept()
86 event.accept()
87 elif reply == 0: # close Console
87 elif reply == 0: # close Console
88 if not self._existing:
88 if not self._existing:
89 # I have the kernel: don't quit, just close the window
89 # I have the kernel: don't quit, just close the window
90 self._app.setQuitOnLastWindowClosed(False)
90 self._app.setQuitOnLastWindowClosed(False)
91 self.deleteLater()
91 self.deleteLater()
92 event.accept()
92 event.accept()
93 else:
93 else:
94 event.ignore()
94 event.ignore()
95 else:
95 else:
96 reply = QtGui.QMessageBox.question(self, title,
96 reply = QtGui.QMessageBox.question(self, title,
97 "Are you sure you want to close this Console?"+
97 "Are you sure you want to close this Console?"+
98 "\nThe Kernel and other Consoles will remain active.",
98 "\nThe Kernel and other Consoles will remain active.",
99 okay|cancel,
99 okay|cancel,
100 defaultButton=okay
100 defaultButton=okay
101 )
101 )
102 if reply == okay:
102 if reply == okay:
103 event.accept()
103 event.accept()
104 else:
104 else:
105 event.ignore()
105 event.ignore()
106
106
107
107
108 #-----------------------------------------------------------------------------
108 #-----------------------------------------------------------------------------
109 # Main entry point
109 # Main entry point
110 #-----------------------------------------------------------------------------
110 #-----------------------------------------------------------------------------
111
111
112 def main():
112 def main():
113 """ Entry point for application.
113 """ Entry point for application.
114 """
114 """
115 # Parse command line arguments.
115 # Parse command line arguments.
116 parser = ArgumentParser()
116 parser = ArgumentParser()
117 kgroup = parser.add_argument_group('kernel options')
117 kgroup = parser.add_argument_group('kernel options')
118 kgroup.add_argument('-e', '--existing', action='store_true',
118 kgroup.add_argument('-e', '--existing', action='store_true',
119 help='connect to an existing kernel')
119 help='connect to an existing kernel')
120 kgroup.add_argument('--ip', type=str, default=LOCALHOST,
120 kgroup.add_argument('--ip', type=str, default=LOCALHOST,
121 help=\
121 help=\
122 "set the kernel\'s IP address [default localhost].\
122 "set the kernel\'s IP address [default localhost].\
123 If the IP address is something other than localhost, then \
123 If the IP address is something other than localhost, then \
124 Consoles on other machines will be able to connect\
124 Consoles on other machines will be able to connect\
125 to the Kernel, so be careful!")
125 to the Kernel, so be careful!")
126 kgroup.add_argument('--xreq', type=int, metavar='PORT', default=0,
126 kgroup.add_argument('--xreq', type=int, metavar='PORT', default=0,
127 help='set the XREQ channel port [default random]')
127 help='set the XREQ channel port [default random]')
128 kgroup.add_argument('--sub', type=int, metavar='PORT', default=0,
128 kgroup.add_argument('--sub', type=int, metavar='PORT', default=0,
129 help='set the SUB channel port [default random]')
129 help='set the SUB channel port [default random]')
130 kgroup.add_argument('--rep', type=int, metavar='PORT', default=0,
130 kgroup.add_argument('--rep', type=int, metavar='PORT', default=0,
131 help='set the REP channel port [default random]')
131 help='set the REP channel port [default random]')
132 kgroup.add_argument('--hb', type=int, metavar='PORT', default=0,
132 kgroup.add_argument('--hb', type=int, metavar='PORT', default=0,
133 help='set the heartbeat port [default random]')
133 help='set the heartbeat port [default random]')
134
134
135 egroup = kgroup.add_mutually_exclusive_group()
135 egroup = kgroup.add_mutually_exclusive_group()
136 egroup.add_argument('--pure', action='store_true', help = \
136 egroup.add_argument('--pure', action='store_true', help = \
137 'use a pure Python kernel instead of an IPython kernel')
137 'use a pure Python kernel instead of an IPython kernel')
138 egroup.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
138 egroup.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
139 const='auto', help = \
139 const='auto', help = \
140 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
140 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
141 given, the GUI backend is matplotlib's, otherwise use one of: \
141 given, the GUI backend is matplotlib's, otherwise use one of: \
142 ['tk', 'gtk', 'qt', 'wx', 'inline'].")
142 ['tk', 'gtk', 'qt', 'wx', 'inline'].")
143
143
144 wgroup = parser.add_argument_group('widget options')
144 wgroup = parser.add_argument_group('widget options')
145 wgroup.add_argument('--paging', type=str, default='inside',
145 wgroup.add_argument('--paging', type=str, default='inside',
146 choices = ['inside', 'hsplit', 'vsplit', 'none'],
146 choices = ['inside', 'hsplit', 'vsplit', 'none'],
147 help='set the paging style [default inside]')
147 help='set the paging style [default inside]')
148 wgroup.add_argument('--rich', action='store_true',
148 wgroup.add_argument('--rich', action='store_true',
149 help='enable rich text support')
149 help='enable rich text support')
150 wgroup.add_argument('--gui-completion', action='store_true',
150 wgroup.add_argument('--gui-completion', action='store_true',
151 help='use a GUI widget for tab completion')
151 help='use a GUI widget for tab completion')
152 wgroup.add_argument('--style', type=str,
152 wgroup.add_argument('--style', type=str,
153 choices = list(get_all_styles()),
153 choices = list(get_all_styles()),
154 help='specify a pygments style for by name.')
154 help='specify a pygments style for by name.')
155 wgroup.add_argument('--stylesheet', type=str,
155 wgroup.add_argument('--stylesheet', type=str,
156 help="path to a custom CSS stylesheet.")
156 help="path to a custom CSS stylesheet.")
157 wgroup.add_argument('--colors', type=str,
157 wgroup.add_argument('--colors', type=str,
158 help="Set the color scheme (light, dark, or bw). This is guessed\
158 help="Set the color scheme (LightBG,Linux,NoColor). This is guessed\
159 based on the pygments style if not set.")
159 based on the pygments style if not set.")
160
160
161 args = parser.parse_args()
161 args = parser.parse_args()
162
162
163 # parse the colors arg down to current known labels
164 if args.colors:
165 colors=args.colors.lower()
166 if colors in ('lightbg', 'light'):
167 colors='lightbg'
168 elif colors in ('dark', 'linux'):
169 colors='linux'
170 else:
171 colors='nocolor'
172 elif args.style:
173 if args.style=='bw':
174 colors='nocolor'
175 elif styles.dark_style(args.style):
176 colors='linux'
177 else:
178 colors='lightbg'
179 else:
180 colors=None
181
163 # Don't let Qt or ZMQ swallow KeyboardInterupts.
182 # Don't let Qt or ZMQ swallow KeyboardInterupts.
164 import signal
183 import signal
165 signal.signal(signal.SIGINT, signal.SIG_DFL)
184 signal.signal(signal.SIGINT, signal.SIG_DFL)
166
185
167 # Create a KernelManager and start a kernel.
186 # Create a KernelManager and start a kernel.
168 kernel_manager = QtKernelManager(xreq_address=(args.ip, args.xreq),
187 kernel_manager = QtKernelManager(xreq_address=(args.ip, args.xreq),
169 sub_address=(args.ip, args.sub),
188 sub_address=(args.ip, args.sub),
170 rep_address=(args.ip, args.rep),
189 rep_address=(args.ip, args.rep),
171 hb_address=(args.ip, args.hb))
190 hb_address=(args.ip, args.hb))
172 if not args.existing:
191 if not args.existing:
173 # if not args.ip in LOCAL_IPS+ALL_ALIAS:
192 # if not args.ip in LOCAL_IPS+ALL_ALIAS:
174 # raise ValueError("Must bind a local ip, such as: %s"%LOCAL_IPS)
193 # raise ValueError("Must bind a local ip, such as: %s"%LOCAL_IPS)
175
194
176 kwargs = dict(ip=args.ip)
195 kwargs = dict(ip=args.ip)
177 if args.pure:
196 if args.pure:
178 kwargs['ipython']=False
197 kwargs['ipython']=False
179 elif args.pylab:
198 else:
180 kwargs['pylab']=args.pylab
199 kwargs['colors']=colors
181
200 if args.pylab:
201 kwargs['pylab']=args.pylab
202
182 kernel_manager.start_kernel(**kwargs)
203 kernel_manager.start_kernel(**kwargs)
183 kernel_manager.start_channels()
204 kernel_manager.start_channels()
184
205
185 local_kernel = (not args.existing) or args.ip in LOCAL_IPS
206 local_kernel = (not args.existing) or args.ip in LOCAL_IPS
186 # Create the widget.
207 # Create the widget.
187 app = QtGui.QApplication([])
208 app = QtGui.QApplication([])
188 if args.pure:
209 if args.pure:
189 kind = 'rich' if args.rich else 'plain'
210 kind = 'rich' if args.rich else 'plain'
190 widget = FrontendWidget(kind=kind, paging=args.paging, local_kernel=local_kernel)
211 widget = FrontendWidget(kind=kind, paging=args.paging, local_kernel=local_kernel)
191 elif args.rich or args.pylab:
212 elif args.rich or args.pylab:
192 widget = RichIPythonWidget(paging=args.paging, local_kernel=local_kernel)
213 widget = RichIPythonWidget(paging=args.paging, local_kernel=local_kernel)
193 else:
214 else:
194 widget = IPythonWidget(paging=args.paging, local_kernel=local_kernel)
215 widget = IPythonWidget(paging=args.paging, local_kernel=local_kernel)
195 widget.gui_completion = args.gui_completion
216 widget.gui_completion = args.gui_completion
196 widget.kernel_manager = kernel_manager
217 widget.kernel_manager = kernel_manager
197
218
198 # configure the style:
219 # configure the style:
199 if not args.pure: # only IPythonWidget supports styles
220 if not args.pure: # only IPythonWidget supports styles
200 # parse the colors arg down to current known labels
201 if args.colors:
202 colors=args.colors.lower()
203 if colors in ('lightbg', 'light'):
204 colors='light'
205 elif colors in ('dark', 'linux'):
206 colors='dark'
207 else:
208 colors='nocolor'
209 else:
210 colors=None
211 lightbg = colors != 'linux'
212
213 if args.style:
221 if args.style:
214 # guess whether it's a dark style:
215 widget.syntax_style = args.style
222 widget.syntax_style = args.style
216 widget.style_sheet = styles.sheet_from_template(args.style, lightbg)
223 widget.style_sheet = styles.sheet_from_template(args.style, colors)
217 widget._syntax_style_changed()
224 widget._syntax_style_changed()
218 widget._style_sheet_changed()
225 widget._style_sheet_changed()
219 elif colors:
226 elif colors:
220 # use a default style
227 # use a default style
221 widget.set_default_style(colors=colors)
228 widget.set_default_style(colors=colors)
222 else:
229 else:
223 # this is redundant for now, but allows the widget's
230 # this is redundant for now, but allows the widget's
224 # defaults to change
231 # defaults to change
225 widget.set_default_style()
232 widget.set_default_style()
226
233
227 if args.stylesheet:
234 if args.stylesheet:
228 # we got an expicit stylesheet
235 # we got an expicit stylesheet
229 if os.path.isfile(args.stylesheet):
236 if os.path.isfile(args.stylesheet):
230 with open(args.stylesheet) as f:
237 with open(args.stylesheet) as f:
231 sheet = f.read()
238 sheet = f.read()
232 widget.style_sheet = sheet
239 widget.style_sheet = sheet
233 widget._style_sheet_changed()
240 widget._style_sheet_changed()
234 else:
241 else:
235 raise IOError("Stylesheet %r not found."%args.stylesheet)
242 raise IOError("Stylesheet %r not found."%args.stylesheet)
236
243
237 # Create the main window.
244 # Create the main window.
238 window = MainWindow(app, widget, args.existing, may_close=local_kernel)
245 window = MainWindow(app, widget, args.existing, may_close=local_kernel)
239 window.setWindowTitle('Python' if args.pure else 'IPython')
246 window.setWindowTitle('Python' if args.pure else 'IPython')
240 window.show()
247 window.show()
241
248
242 # Start the application main loop.
249 # Start the application main loop.
243 app.exec_()
250 app.exec_()
244
251
245
252
246 if __name__ == '__main__':
253 if __name__ == '__main__':
247 main()
254 main()
@@ -1,119 +1,119 b''
1 """ Style utilities, templates, and defaults for syntax highlighting widgets.
1 """ Style utilities, templates, and defaults for syntax highlighting widgets.
2 """
2 """
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Imports
4 # Imports
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6
6
7 from colorsys import rgb_to_hls
7 from colorsys import rgb_to_hls
8 from pygments.styles import get_style_by_name
8 from pygments.styles import get_style_by_name
9 from pygments.token import Token
9 from pygments.token import Token
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Constants
12 # Constants
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 # The default light style sheet: black text on a white background.
15 # The default light style sheet: black text on a white background.
16 default_light_style_template = '''
16 default_light_style_template = '''
17 QPlainTextEdit, QTextEdit { background-color: %(bgcolor)s;
17 QPlainTextEdit, QTextEdit { background-color: %(bgcolor)s;
18 color: %(fgcolor)s ;
18 color: %(fgcolor)s ;
19 selection-background-color: %(select)s}
19 selection-background-color: %(select)s}
20 .error { color: red; }
20 .error { color: red; }
21 .in-prompt { color: navy; }
21 .in-prompt { color: navy; }
22 .in-prompt-number { font-weight: bold; }
22 .in-prompt-number { font-weight: bold; }
23 .out-prompt { color: darkred; }
23 .out-prompt { color: darkred; }
24 .out-prompt-number { font-weight: bold; }
24 .out-prompt-number { font-weight: bold; }
25 '''
25 '''
26 default_light_style_sheet = default_light_style_template%dict(
26 default_light_style_sheet = default_light_style_template%dict(
27 bgcolor='white', fgcolor='black', select="#ccc")
27 bgcolor='white', fgcolor='black', select="#ccc")
28 default_light_syntax_style = 'default'
28 default_light_syntax_style = 'default'
29
29
30 # The default dark style sheet: white text on a black background.
30 # The default dark style sheet: white text on a black background.
31 default_dark_style_template = '''
31 default_dark_style_template = '''
32 QPlainTextEdit, QTextEdit { background-color: %(bgcolor)s;
32 QPlainTextEdit, QTextEdit { background-color: %(bgcolor)s;
33 color: %(fgcolor)s ;
33 color: %(fgcolor)s ;
34 selection-background-color: %(select)s}
34 selection-background-color: %(select)s}
35 QFrame { border: 1px solid grey; }
35 QFrame { border: 1px solid grey; }
36 .error { color: red; }
36 .error { color: red; }
37 .in-prompt { color: lime; }
37 .in-prompt { color: lime; }
38 .in-prompt-number { color: lime; font-weight: bold; }
38 .in-prompt-number { color: lime; font-weight: bold; }
39 .out-prompt { color: red; }
39 .out-prompt { color: red; }
40 .out-prompt-number { color: red; font-weight: bold; }
40 .out-prompt-number { color: red; font-weight: bold; }
41 '''
41 '''
42 default_dark_style_sheet = default_dark_style_template%dict(
42 default_dark_style_sheet = default_dark_style_template%dict(
43 bgcolor='black', fgcolor='white', select="#555")
43 bgcolor='black', fgcolor='white', select="#555")
44 default_dark_syntax_style = 'monokai'
44 default_dark_syntax_style = 'monokai'
45
45
46 # The default monochrome
46 # The default monochrome
47 default_bw_style_sheet = '''
47 default_bw_style_sheet = '''
48 QPlainTextEdit, QTextEdit { background-color: white;
48 QPlainTextEdit, QTextEdit { background-color: white;
49 color: black ;
49 color: black ;
50 selection-background-color: #cccccc}
50 selection-background-color: #cccccc}
51 .in-prompt-number { font-weight: bold; }
51 .in-prompt-number { font-weight: bold; }
52 .out-prompt-number { font-weight: bold; }
52 .out-prompt-number { font-weight: bold; }
53 '''
53 '''
54 default_bw_syntax_style = 'bw'
54 default_bw_syntax_style = 'bw'
55
55
56
56
57 def hex_to_rgb(color):
57 def hex_to_rgb(color):
58 """Convert a hex color to rgb integer tuple."""
58 """Convert a hex color to rgb integer tuple."""
59 if color.startswith('#'):
59 if color.startswith('#'):
60 color = color[1:]
60 color = color[1:]
61 if len(color) == 3:
61 if len(color) == 3:
62 color = ''.join([c*2 for c in color])
62 color = ''.join([c*2 for c in color])
63 if len(color) != 6:
63 if len(color) != 6:
64 return False
64 return False
65 try:
65 try:
66 r = int(color[:2],16)
66 r = int(color[:2],16)
67 g = int(color[:2],16)
67 g = int(color[:2],16)
68 b = int(color[:2],16)
68 b = int(color[:2],16)
69 except ValueError:
69 except ValueError:
70 return False
70 return False
71 else:
71 else:
72 return r,g,b
72 return r,g,b
73
73
74 def dark_color(color):
74 def dark_color(color):
75 """Check whether a color is 'dark'.
75 """Check whether a color is 'dark'.
76
76
77 Currently, this is simply whether the luminance is <50%"""
77 Currently, this is simply whether the luminance is <50%"""
78 rgb = hex_to_rgb(color)
78 rgb = hex_to_rgb(color)
79 if rgb:
79 if rgb:
80 return rgb_to_hls(*rgb)[1] < 128
80 return rgb_to_hls(*rgb)[1] < 128
81 else: # default to False
81 else: # default to False
82 return False
82 return False
83
83
84 def dark_style(stylename):
84 def dark_style(stylename):
85 """Guess whether the background of the style with name 'stylename'
85 """Guess whether the background of the style with name 'stylename'
86 counts as 'dark'."""
86 counts as 'dark'."""
87 return dark_color(get_style_by_name(stylename).background_color)
87 return dark_color(get_style_by_name(stylename).background_color)
88
88
89 def get_colors(stylename):
89 def get_colors(stylename):
90 """Construct the keys to be used building the base stylesheet
90 """Construct the keys to be used building the base stylesheet
91 from a templatee."""
91 from a templatee."""
92 style = get_style_by_name(stylename)
92 style = get_style_by_name(stylename)
93 fgcolor = style.style_for_token(Token.Text)['color'] or ''
93 fgcolor = style.style_for_token(Token.Text)['color'] or ''
94 if len(fgcolor) in (3,6):
94 if len(fgcolor) in (3,6):
95 # could be 'abcdef' or 'ace' hex, which needs '#' prefix
95 # could be 'abcdef' or 'ace' hex, which needs '#' prefix
96 try:
96 try:
97 int(fgcolor, 16)
97 int(fgcolor, 16)
98 except TypeError:
98 except TypeError:
99 pass
99 pass
100 else:
100 else:
101 fgcolor = "#"+fgcolor
101 fgcolor = "#"+fgcolor
102
102
103 return dict(
103 return dict(
104 bgcolor = style.background_color,
104 bgcolor = style.background_color,
105 select = style.highlight_color,
105 select = style.highlight_color,
106 fgcolor = fgcolor
106 fgcolor = fgcolor
107 )
107 )
108
108
109 def sheet_from_template(name, colors='light'):
109 def sheet_from_template(name, colors='lightbg'):
110 """Use one of the base templates, and set bg/fg/select colors."""
110 """Use one of the base templates, and set bg/fg/select colors."""
111 colors = colors.lower()
111 colors = colors.lower()
112 if colors=='light':
112 if colors=='lightbg':
113 return default_light_style_template%get_colors(name)
113 return default_light_style_template%get_colors(name)
114 elif colors=='dark':
114 elif colors=='linux':
115 return default_dark_style_template%get_colors(name)
115 return default_dark_style_template%get_colors(name)
116 elif colors=='nocolor':
116 elif colors=='nocolor':
117 return default_bw_style_sheet
117 return default_bw_style_sheet
118 else:
118 else:
119 raise KeyError("No such color scheme: %s"%colors)
119 raise KeyError("No such color scheme: %s"%colors)
@@ -1,630 +1,642 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """A simple interactive kernel that talks to a frontend over 0MQ.
2 """A simple interactive kernel that talks to a frontend over 0MQ.
3
3
4 Things to do:
4 Things to do:
5
5
6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
7 call set_parent on all the PUB objects with the message about to be executed.
7 call set_parent on all the PUB objects with the message about to be executed.
8 * Implement random port and security key logic.
8 * Implement random port and security key logic.
9 * Implement control messages.
9 * Implement control messages.
10 * Implement event loop and poll version.
10 * Implement event loop and poll version.
11 """
11 """
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from __future__ import print_function
16 from __future__ import print_function
17
17
18 # Standard library imports.
18 # Standard library imports.
19 import __builtin__
19 import __builtin__
20 import atexit
20 import atexit
21 import sys
21 import sys
22 import time
22 import time
23 import traceback
23 import traceback
24
24
25 # System library imports.
25 # System library imports.
26 import zmq
26 import zmq
27
27
28 # Local imports.
28 # Local imports.
29 from IPython.config.configurable import Configurable
29 from IPython.config.configurable import Configurable
30 from IPython.utils import io
30 from IPython.utils import io
31 from IPython.utils.jsonutil import json_clean
31 from IPython.utils.jsonutil import json_clean
32 from IPython.lib import pylabtools
32 from IPython.lib import pylabtools
33 from IPython.utils.traitlets import Instance, Float
33 from IPython.utils.traitlets import Instance, Float
34 from entry_point import (base_launch_kernel, make_argument_parser, make_kernel,
34 from entry_point import (base_launch_kernel, make_argument_parser, make_kernel,
35 start_kernel)
35 start_kernel)
36 from iostream import OutStream
36 from iostream import OutStream
37 from session import Session, Message
37 from session import Session, Message
38 from zmqshell import ZMQInteractiveShell
38 from zmqshell import ZMQInteractiveShell
39
39
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41 # Main kernel class
41 # Main kernel class
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43
43
44 class Kernel(Configurable):
44 class Kernel(Configurable):
45
45
46 #---------------------------------------------------------------------------
46 #---------------------------------------------------------------------------
47 # Kernel interface
47 # Kernel interface
48 #---------------------------------------------------------------------------
48 #---------------------------------------------------------------------------
49
49
50 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
50 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
51 session = Instance(Session)
51 session = Instance(Session)
52 reply_socket = Instance('zmq.Socket')
52 reply_socket = Instance('zmq.Socket')
53 pub_socket = Instance('zmq.Socket')
53 pub_socket = Instance('zmq.Socket')
54 req_socket = Instance('zmq.Socket')
54 req_socket = Instance('zmq.Socket')
55
55
56 # Private interface
56 # Private interface
57
57
58 # Time to sleep after flushing the stdout/err buffers in each execute
58 # Time to sleep after flushing the stdout/err buffers in each execute
59 # cycle. While this introduces a hard limit on the minimal latency of the
59 # cycle. While this introduces a hard limit on the minimal latency of the
60 # execute cycle, it helps prevent output synchronization problems for
60 # execute cycle, it helps prevent output synchronization problems for
61 # clients.
61 # clients.
62 # Units are in seconds. The minimum zmq latency on local host is probably
62 # Units are in seconds. The minimum zmq latency on local host is probably
63 # ~150 microseconds, set this to 500us for now. We may need to increase it
63 # ~150 microseconds, set this to 500us for now. We may need to increase it
64 # a little if it's not enough after more interactive testing.
64 # a little if it's not enough after more interactive testing.
65 _execute_sleep = Float(0.0005, config=True)
65 _execute_sleep = Float(0.0005, config=True)
66
66
67 # Frequency of the kernel's event loop.
67 # Frequency of the kernel's event loop.
68 # Units are in seconds, kernel subclasses for GUI toolkits may need to
68 # Units are in seconds, kernel subclasses for GUI toolkits may need to
69 # adapt to milliseconds.
69 # adapt to milliseconds.
70 _poll_interval = Float(0.05, config=True)
70 _poll_interval = Float(0.05, config=True)
71
71
72 # If the shutdown was requested over the network, we leave here the
72 # If the shutdown was requested over the network, we leave here the
73 # necessary reply message so it can be sent by our registered atexit
73 # necessary reply message so it can be sent by our registered atexit
74 # handler. This ensures that the reply is only sent to clients truly at
74 # handler. This ensures that the reply is only sent to clients truly at
75 # the end of our shutdown process (which happens after the underlying
75 # the end of our shutdown process (which happens after the underlying
76 # IPython shell's own shutdown).
76 # IPython shell's own shutdown).
77 _shutdown_message = None
77 _shutdown_message = None
78
78
79 # This is a dict of port number that the kernel is listening on. It is set
79 # This is a dict of port number that the kernel is listening on. It is set
80 # by record_ports and used by connect_request.
80 # by record_ports and used by connect_request.
81 _recorded_ports = None
81 _recorded_ports = None
82
82
83 def __init__(self, **kwargs):
83 def __init__(self, **kwargs):
84 super(Kernel, self).__init__(**kwargs)
84 super(Kernel, self).__init__(**kwargs)
85
85
86 # Before we even start up the shell, register *first* our exit handlers
86 # Before we even start up the shell, register *first* our exit handlers
87 # so they come before the shell's
87 # so they come before the shell's
88 atexit.register(self._at_shutdown)
88 atexit.register(self._at_shutdown)
89
89
90 # Initialize the InteractiveShell subclass
90 # Initialize the InteractiveShell subclass
91 self.shell = ZMQInteractiveShell.instance()
91 self.shell = ZMQInteractiveShell.instance()
92 self.shell.displayhook.session = self.session
92 self.shell.displayhook.session = self.session
93 self.shell.displayhook.pub_socket = self.pub_socket
93 self.shell.displayhook.pub_socket = self.pub_socket
94
94
95 # TMP - hack while developing
95 # TMP - hack while developing
96 self.shell._reply_content = None
96 self.shell._reply_content = None
97
97
98 # Build dict of handlers for message types
98 # Build dict of handlers for message types
99 msg_types = [ 'execute_request', 'complete_request',
99 msg_types = [ 'execute_request', 'complete_request',
100 'object_info_request', 'history_request',
100 'object_info_request', 'history_request',
101 'connect_request', 'shutdown_request']
101 'connect_request', 'shutdown_request']
102 self.handlers = {}
102 self.handlers = {}
103 for msg_type in msg_types:
103 for msg_type in msg_types:
104 self.handlers[msg_type] = getattr(self, msg_type)
104 self.handlers[msg_type] = getattr(self, msg_type)
105
105
106 def do_one_iteration(self):
106 def do_one_iteration(self):
107 """Do one iteration of the kernel's evaluation loop.
107 """Do one iteration of the kernel's evaluation loop.
108 """
108 """
109 try:
109 try:
110 ident = self.reply_socket.recv(zmq.NOBLOCK)
110 ident = self.reply_socket.recv(zmq.NOBLOCK)
111 except zmq.ZMQError, e:
111 except zmq.ZMQError, e:
112 if e.errno == zmq.EAGAIN:
112 if e.errno == zmq.EAGAIN:
113 return
113 return
114 else:
114 else:
115 raise
115 raise
116 # This assert will raise in versions of zeromq 2.0.7 and lesser.
116 # This assert will raise in versions of zeromq 2.0.7 and lesser.
117 # We now require 2.0.8 or above, so we can uncomment for safety.
117 # We now require 2.0.8 or above, so we can uncomment for safety.
118 assert self.reply_socket.rcvmore(), "Missing message part."
118 assert self.reply_socket.rcvmore(), "Missing message part."
119 msg = self.reply_socket.recv_json()
119 msg = self.reply_socket.recv_json()
120
120
121 # Print some info about this message and leave a '--->' marker, so it's
121 # Print some info about this message and leave a '--->' marker, so it's
122 # easier to trace visually the message chain when debugging. Each
122 # easier to trace visually the message chain when debugging. Each
123 # handler prints its message at the end.
123 # handler prints its message at the end.
124 # Eventually we'll move these from stdout to a logger.
124 # Eventually we'll move these from stdout to a logger.
125 io.raw_print('\n*** MESSAGE TYPE:', msg['msg_type'], '***')
125 io.raw_print('\n*** MESSAGE TYPE:', msg['msg_type'], '***')
126 io.raw_print(' Content: ', msg['content'],
126 io.raw_print(' Content: ', msg['content'],
127 '\n --->\n ', sep='', end='')
127 '\n --->\n ', sep='', end='')
128
128
129 # Find and call actual handler for message
129 # Find and call actual handler for message
130 handler = self.handlers.get(msg['msg_type'], None)
130 handler = self.handlers.get(msg['msg_type'], None)
131 if handler is None:
131 if handler is None:
132 io.raw_print_err("UNKNOWN MESSAGE TYPE:", msg)
132 io.raw_print_err("UNKNOWN MESSAGE TYPE:", msg)
133 else:
133 else:
134 handler(ident, msg)
134 handler(ident, msg)
135
135
136 # Check whether we should exit, in case the incoming message set the
136 # Check whether we should exit, in case the incoming message set the
137 # exit flag on
137 # exit flag on
138 if self.shell.exit_now:
138 if self.shell.exit_now:
139 io.raw_print('\nExiting IPython kernel...')
139 io.raw_print('\nExiting IPython kernel...')
140 # We do a normal, clean exit, which allows any actions registered
140 # We do a normal, clean exit, which allows any actions registered
141 # via atexit (such as history saving) to take place.
141 # via atexit (such as history saving) to take place.
142 sys.exit(0)
142 sys.exit(0)
143
143
144
144
145 def start(self):
145 def start(self):
146 """ Start the kernel main loop.
146 """ Start the kernel main loop.
147 """
147 """
148 while True:
148 while True:
149 time.sleep(self._poll_interval)
149 time.sleep(self._poll_interval)
150 self.do_one_iteration()
150 self.do_one_iteration()
151
151
152 def record_ports(self, xrep_port, pub_port, req_port, hb_port):
152 def record_ports(self, xrep_port, pub_port, req_port, hb_port):
153 """Record the ports that this kernel is using.
153 """Record the ports that this kernel is using.
154
154
155 The creator of the Kernel instance must call this methods if they
155 The creator of the Kernel instance must call this methods if they
156 want the :meth:`connect_request` method to return the port numbers.
156 want the :meth:`connect_request` method to return the port numbers.
157 """
157 """
158 self._recorded_ports = {
158 self._recorded_ports = {
159 'xrep_port' : xrep_port,
159 'xrep_port' : xrep_port,
160 'pub_port' : pub_port,
160 'pub_port' : pub_port,
161 'req_port' : req_port,
161 'req_port' : req_port,
162 'hb_port' : hb_port
162 'hb_port' : hb_port
163 }
163 }
164
164
165 #---------------------------------------------------------------------------
165 #---------------------------------------------------------------------------
166 # Kernel request handlers
166 # Kernel request handlers
167 #---------------------------------------------------------------------------
167 #---------------------------------------------------------------------------
168
168
169 def _publish_pyin(self, code, parent):
169 def _publish_pyin(self, code, parent):
170 """Publish the code request on the pyin stream."""
170 """Publish the code request on the pyin stream."""
171
171
172 pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
172 pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
173 self.pub_socket.send_json(pyin_msg)
173 self.pub_socket.send_json(pyin_msg)
174
174
175 def execute_request(self, ident, parent):
175 def execute_request(self, ident, parent):
176
176
177 status_msg = self.session.msg(
177 status_msg = self.session.msg(
178 u'status',
178 u'status',
179 {u'execution_state':u'busy'},
179 {u'execution_state':u'busy'},
180 parent=parent
180 parent=parent
181 )
181 )
182 self.pub_socket.send_json(status_msg)
182 self.pub_socket.send_json(status_msg)
183
183
184 try:
184 try:
185 content = parent[u'content']
185 content = parent[u'content']
186 code = content[u'code']
186 code = content[u'code']
187 silent = content[u'silent']
187 silent = content[u'silent']
188 except:
188 except:
189 io.raw_print_err("Got bad msg: ")
189 io.raw_print_err("Got bad msg: ")
190 io.raw_print_err(Message(parent))
190 io.raw_print_err(Message(parent))
191 return
191 return
192
192
193 shell = self.shell # we'll need this a lot here
193 shell = self.shell # we'll need this a lot here
194
194
195 # Replace raw_input. Note that is not sufficient to replace
195 # Replace raw_input. Note that is not sufficient to replace
196 # raw_input in the user namespace.
196 # raw_input in the user namespace.
197 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
197 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
198 __builtin__.raw_input = raw_input
198 __builtin__.raw_input = raw_input
199
199
200 # Set the parent message of the display hook and out streams.
200 # Set the parent message of the display hook and out streams.
201 shell.displayhook.set_parent(parent)
201 shell.displayhook.set_parent(parent)
202 sys.stdout.set_parent(parent)
202 sys.stdout.set_parent(parent)
203 sys.stderr.set_parent(parent)
203 sys.stderr.set_parent(parent)
204
204
205 # Re-broadcast our input for the benefit of listening clients, and
205 # Re-broadcast our input for the benefit of listening clients, and
206 # start computing output
206 # start computing output
207 if not silent:
207 if not silent:
208 self._publish_pyin(code, parent)
208 self._publish_pyin(code, parent)
209
209
210 reply_content = {}
210 reply_content = {}
211 try:
211 try:
212 if silent:
212 if silent:
213 # run_code uses 'exec' mode, so no displayhook will fire, and it
213 # run_code uses 'exec' mode, so no displayhook will fire, and it
214 # doesn't call logging or history manipulations. Print
214 # doesn't call logging or history manipulations. Print
215 # statements in that code will obviously still execute.
215 # statements in that code will obviously still execute.
216 shell.run_code(code)
216 shell.run_code(code)
217 else:
217 else:
218 # FIXME: the shell calls the exception handler itself.
218 # FIXME: the shell calls the exception handler itself.
219 shell._reply_content = None
219 shell._reply_content = None
220 shell.run_cell(code)
220 shell.run_cell(code)
221 except:
221 except:
222 status = u'error'
222 status = u'error'
223 # FIXME: this code right now isn't being used yet by default,
223 # FIXME: this code right now isn't being used yet by default,
224 # because the runlines() call above directly fires off exception
224 # because the runlines() call above directly fires off exception
225 # reporting. This code, therefore, is only active in the scenario
225 # reporting. This code, therefore, is only active in the scenario
226 # where runlines itself has an unhandled exception. We need to
226 # where runlines itself has an unhandled exception. We need to
227 # uniformize this, for all exception construction to come from a
227 # uniformize this, for all exception construction to come from a
228 # single location in the codbase.
228 # single location in the codbase.
229 etype, evalue, tb = sys.exc_info()
229 etype, evalue, tb = sys.exc_info()
230 tb_list = traceback.format_exception(etype, evalue, tb)
230 tb_list = traceback.format_exception(etype, evalue, tb)
231 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
231 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
232 else:
232 else:
233 status = u'ok'
233 status = u'ok'
234
234
235 reply_content[u'status'] = status
235 reply_content[u'status'] = status
236
236
237 # Return the execution counter so clients can display prompts
237 # Return the execution counter so clients can display prompts
238 reply_content['execution_count'] = shell.execution_count -1
238 reply_content['execution_count'] = shell.execution_count -1
239
239
240 # FIXME - fish exception info out of shell, possibly left there by
240 # FIXME - fish exception info out of shell, possibly left there by
241 # runlines. We'll need to clean up this logic later.
241 # runlines. We'll need to clean up this logic later.
242 if shell._reply_content is not None:
242 if shell._reply_content is not None:
243 reply_content.update(shell._reply_content)
243 reply_content.update(shell._reply_content)
244
244
245 # At this point, we can tell whether the main code execution succeeded
245 # At this point, we can tell whether the main code execution succeeded
246 # or not. If it did, we proceed to evaluate user_variables/expressions
246 # or not. If it did, we proceed to evaluate user_variables/expressions
247 if reply_content['status'] == 'ok':
247 if reply_content['status'] == 'ok':
248 reply_content[u'user_variables'] = \
248 reply_content[u'user_variables'] = \
249 shell.user_variables(content[u'user_variables'])
249 shell.user_variables(content[u'user_variables'])
250 reply_content[u'user_expressions'] = \
250 reply_content[u'user_expressions'] = \
251 shell.user_expressions(content[u'user_expressions'])
251 shell.user_expressions(content[u'user_expressions'])
252 else:
252 else:
253 # If there was an error, don't even try to compute variables or
253 # If there was an error, don't even try to compute variables or
254 # expressions
254 # expressions
255 reply_content[u'user_variables'] = {}
255 reply_content[u'user_variables'] = {}
256 reply_content[u'user_expressions'] = {}
256 reply_content[u'user_expressions'] = {}
257
257
258 # Payloads should be retrieved regardless of outcome, so we can both
258 # Payloads should be retrieved regardless of outcome, so we can both
259 # recover partial output (that could have been generated early in a
259 # recover partial output (that could have been generated early in a
260 # block, before an error) and clear the payload system always.
260 # block, before an error) and clear the payload system always.
261 reply_content[u'payload'] = shell.payload_manager.read_payload()
261 reply_content[u'payload'] = shell.payload_manager.read_payload()
262 # Be agressive about clearing the payload because we don't want
262 # Be agressive about clearing the payload because we don't want
263 # it to sit in memory until the next execute_request comes in.
263 # it to sit in memory until the next execute_request comes in.
264 shell.payload_manager.clear_payload()
264 shell.payload_manager.clear_payload()
265
265
266 # Send the reply.
266 # Send the reply.
267 reply_msg = self.session.msg(u'execute_reply', reply_content, parent)
267 reply_msg = self.session.msg(u'execute_reply', reply_content, parent)
268 io.raw_print(reply_msg)
268 io.raw_print(reply_msg)
269
269
270 # Flush output before sending the reply.
270 # Flush output before sending the reply.
271 sys.stdout.flush()
271 sys.stdout.flush()
272 sys.stderr.flush()
272 sys.stderr.flush()
273 # FIXME: on rare occasions, the flush doesn't seem to make it to the
273 # FIXME: on rare occasions, the flush doesn't seem to make it to the
274 # clients... This seems to mitigate the problem, but we definitely need
274 # clients... This seems to mitigate the problem, but we definitely need
275 # to better understand what's going on.
275 # to better understand what's going on.
276 if self._execute_sleep:
276 if self._execute_sleep:
277 time.sleep(self._execute_sleep)
277 time.sleep(self._execute_sleep)
278
278
279 self.reply_socket.send(ident, zmq.SNDMORE)
279 self.reply_socket.send(ident, zmq.SNDMORE)
280 self.reply_socket.send_json(reply_msg)
280 self.reply_socket.send_json(reply_msg)
281 if reply_msg['content']['status'] == u'error':
281 if reply_msg['content']['status'] == u'error':
282 self._abort_queue()
282 self._abort_queue()
283
283
284 status_msg = self.session.msg(
284 status_msg = self.session.msg(
285 u'status',
285 u'status',
286 {u'execution_state':u'idle'},
286 {u'execution_state':u'idle'},
287 parent=parent
287 parent=parent
288 )
288 )
289 self.pub_socket.send_json(status_msg)
289 self.pub_socket.send_json(status_msg)
290
290
291 def complete_request(self, ident, parent):
291 def complete_request(self, ident, parent):
292 txt, matches = self._complete(parent)
292 txt, matches = self._complete(parent)
293 matches = {'matches' : matches,
293 matches = {'matches' : matches,
294 'matched_text' : txt,
294 'matched_text' : txt,
295 'status' : 'ok'}
295 'status' : 'ok'}
296 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
296 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
297 matches, parent, ident)
297 matches, parent, ident)
298 io.raw_print(completion_msg)
298 io.raw_print(completion_msg)
299
299
300 def object_info_request(self, ident, parent):
300 def object_info_request(self, ident, parent):
301 object_info = self.shell.object_inspect(parent['content']['oname'])
301 object_info = self.shell.object_inspect(parent['content']['oname'])
302 # Before we send this object over, we scrub it for JSON usage
302 # Before we send this object over, we scrub it for JSON usage
303 oinfo = json_clean(object_info)
303 oinfo = json_clean(object_info)
304 msg = self.session.send(self.reply_socket, 'object_info_reply',
304 msg = self.session.send(self.reply_socket, 'object_info_reply',
305 oinfo, parent, ident)
305 oinfo, parent, ident)
306 io.raw_print(msg)
306 io.raw_print(msg)
307
307
308 def history_request(self, ident, parent):
308 def history_request(self, ident, parent):
309 output = parent['content']['output']
309 output = parent['content']['output']
310 index = parent['content']['index']
310 index = parent['content']['index']
311 raw = parent['content']['raw']
311 raw = parent['content']['raw']
312 hist = self.shell.get_history(index=index, raw=raw, output=output)
312 hist = self.shell.get_history(index=index, raw=raw, output=output)
313 content = {'history' : hist}
313 content = {'history' : hist}
314 msg = self.session.send(self.reply_socket, 'history_reply',
314 msg = self.session.send(self.reply_socket, 'history_reply',
315 content, parent, ident)
315 content, parent, ident)
316 io.raw_print(msg)
316 io.raw_print(msg)
317
317
318 def connect_request(self, ident, parent):
318 def connect_request(self, ident, parent):
319 if self._recorded_ports is not None:
319 if self._recorded_ports is not None:
320 content = self._recorded_ports.copy()
320 content = self._recorded_ports.copy()
321 else:
321 else:
322 content = {}
322 content = {}
323 msg = self.session.send(self.reply_socket, 'connect_reply',
323 msg = self.session.send(self.reply_socket, 'connect_reply',
324 content, parent, ident)
324 content, parent, ident)
325 io.raw_print(msg)
325 io.raw_print(msg)
326
326
327 def shutdown_request(self, ident, parent):
327 def shutdown_request(self, ident, parent):
328 self.shell.exit_now = True
328 self.shell.exit_now = True
329 self._shutdown_message = self.session.msg(u'shutdown_reply', parent['content'], parent)
329 self._shutdown_message = self.session.msg(u'shutdown_reply', parent['content'], parent)
330 sys.exit(0)
330 sys.exit(0)
331
331
332 #---------------------------------------------------------------------------
332 #---------------------------------------------------------------------------
333 # Protected interface
333 # Protected interface
334 #---------------------------------------------------------------------------
334 #---------------------------------------------------------------------------
335
335
336 def _abort_queue(self):
336 def _abort_queue(self):
337 while True:
337 while True:
338 try:
338 try:
339 ident = self.reply_socket.recv(zmq.NOBLOCK)
339 ident = self.reply_socket.recv(zmq.NOBLOCK)
340 except zmq.ZMQError, e:
340 except zmq.ZMQError, e:
341 if e.errno == zmq.EAGAIN:
341 if e.errno == zmq.EAGAIN:
342 break
342 break
343 else:
343 else:
344 assert self.reply_socket.rcvmore(), \
344 assert self.reply_socket.rcvmore(), \
345 "Unexpected missing message part."
345 "Unexpected missing message part."
346 msg = self.reply_socket.recv_json()
346 msg = self.reply_socket.recv_json()
347 io.raw_print("Aborting:\n", Message(msg))
347 io.raw_print("Aborting:\n", Message(msg))
348 msg_type = msg['msg_type']
348 msg_type = msg['msg_type']
349 reply_type = msg_type.split('_')[0] + '_reply'
349 reply_type = msg_type.split('_')[0] + '_reply'
350 reply_msg = self.session.msg(reply_type, {'status' : 'aborted'}, msg)
350 reply_msg = self.session.msg(reply_type, {'status' : 'aborted'}, msg)
351 io.raw_print(reply_msg)
351 io.raw_print(reply_msg)
352 self.reply_socket.send(ident,zmq.SNDMORE)
352 self.reply_socket.send(ident,zmq.SNDMORE)
353 self.reply_socket.send_json(reply_msg)
353 self.reply_socket.send_json(reply_msg)
354 # We need to wait a bit for requests to come in. This can probably
354 # We need to wait a bit for requests to come in. This can probably
355 # be set shorter for true asynchronous clients.
355 # be set shorter for true asynchronous clients.
356 time.sleep(0.1)
356 time.sleep(0.1)
357
357
358 def _raw_input(self, prompt, ident, parent):
358 def _raw_input(self, prompt, ident, parent):
359 # Flush output before making the request.
359 # Flush output before making the request.
360 sys.stderr.flush()
360 sys.stderr.flush()
361 sys.stdout.flush()
361 sys.stdout.flush()
362
362
363 # Send the input request.
363 # Send the input request.
364 content = dict(prompt=prompt)
364 content = dict(prompt=prompt)
365 msg = self.session.msg(u'input_request', content, parent)
365 msg = self.session.msg(u'input_request', content, parent)
366 self.req_socket.send_json(msg)
366 self.req_socket.send_json(msg)
367
367
368 # Await a response.
368 # Await a response.
369 reply = self.req_socket.recv_json()
369 reply = self.req_socket.recv_json()
370 try:
370 try:
371 value = reply['content']['value']
371 value = reply['content']['value']
372 except:
372 except:
373 io.raw_print_err("Got bad raw_input reply: ")
373 io.raw_print_err("Got bad raw_input reply: ")
374 io.raw_print_err(Message(parent))
374 io.raw_print_err(Message(parent))
375 value = ''
375 value = ''
376 return value
376 return value
377
377
378 def _complete(self, msg):
378 def _complete(self, msg):
379 c = msg['content']
379 c = msg['content']
380 try:
380 try:
381 cpos = int(c['cursor_pos'])
381 cpos = int(c['cursor_pos'])
382 except:
382 except:
383 # If we don't get something that we can convert to an integer, at
383 # If we don't get something that we can convert to an integer, at
384 # least attempt the completion guessing the cursor is at the end of
384 # least attempt the completion guessing the cursor is at the end of
385 # the text, if there's any, and otherwise of the line
385 # the text, if there's any, and otherwise of the line
386 cpos = len(c['text'])
386 cpos = len(c['text'])
387 if cpos==0:
387 if cpos==0:
388 cpos = len(c['line'])
388 cpos = len(c['line'])
389 return self.shell.complete(c['text'], c['line'], cpos)
389 return self.shell.complete(c['text'], c['line'], cpos)
390
390
391 def _object_info(self, context):
391 def _object_info(self, context):
392 symbol, leftover = self._symbol_from_context(context)
392 symbol, leftover = self._symbol_from_context(context)
393 if symbol is not None and not leftover:
393 if symbol is not None and not leftover:
394 doc = getattr(symbol, '__doc__', '')
394 doc = getattr(symbol, '__doc__', '')
395 else:
395 else:
396 doc = ''
396 doc = ''
397 object_info = dict(docstring = doc)
397 object_info = dict(docstring = doc)
398 return object_info
398 return object_info
399
399
400 def _symbol_from_context(self, context):
400 def _symbol_from_context(self, context):
401 if not context:
401 if not context:
402 return None, context
402 return None, context
403
403
404 base_symbol_string = context[0]
404 base_symbol_string = context[0]
405 symbol = self.shell.user_ns.get(base_symbol_string, None)
405 symbol = self.shell.user_ns.get(base_symbol_string, None)
406 if symbol is None:
406 if symbol is None:
407 symbol = __builtin__.__dict__.get(base_symbol_string, None)
407 symbol = __builtin__.__dict__.get(base_symbol_string, None)
408 if symbol is None:
408 if symbol is None:
409 return None, context
409 return None, context
410
410
411 context = context[1:]
411 context = context[1:]
412 for i, name in enumerate(context):
412 for i, name in enumerate(context):
413 new_symbol = getattr(symbol, name, None)
413 new_symbol = getattr(symbol, name, None)
414 if new_symbol is None:
414 if new_symbol is None:
415 return symbol, context[i:]
415 return symbol, context[i:]
416 else:
416 else:
417 symbol = new_symbol
417 symbol = new_symbol
418
418
419 return symbol, []
419 return symbol, []
420
420
421 def _at_shutdown(self):
421 def _at_shutdown(self):
422 """Actions taken at shutdown by the kernel, called by python's atexit.
422 """Actions taken at shutdown by the kernel, called by python's atexit.
423 """
423 """
424 # io.rprint("Kernel at_shutdown") # dbg
424 # io.rprint("Kernel at_shutdown") # dbg
425 if self._shutdown_message is not None:
425 if self._shutdown_message is not None:
426 self.reply_socket.send_json(self._shutdown_message)
426 self.reply_socket.send_json(self._shutdown_message)
427 self.pub_socket.send_json(self._shutdown_message)
427 self.pub_socket.send_json(self._shutdown_message)
428 io.raw_print(self._shutdown_message)
428 io.raw_print(self._shutdown_message)
429 # A very short sleep to give zmq time to flush its message buffers
429 # A very short sleep to give zmq time to flush its message buffers
430 # before Python truly shuts down.
430 # before Python truly shuts down.
431 time.sleep(0.01)
431 time.sleep(0.01)
432
432
433
433
434 class QtKernel(Kernel):
434 class QtKernel(Kernel):
435 """A Kernel subclass with Qt support."""
435 """A Kernel subclass with Qt support."""
436
436
437 def start(self):
437 def start(self):
438 """Start a kernel with QtPy4 event loop integration."""
438 """Start a kernel with QtPy4 event loop integration."""
439
439
440 from PyQt4 import QtCore
440 from PyQt4 import QtCore
441 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
441 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
442
442
443 self.app = get_app_qt4([" "])
443 self.app = get_app_qt4([" "])
444 self.app.setQuitOnLastWindowClosed(False)
444 self.app.setQuitOnLastWindowClosed(False)
445 self.timer = QtCore.QTimer()
445 self.timer = QtCore.QTimer()
446 self.timer.timeout.connect(self.do_one_iteration)
446 self.timer.timeout.connect(self.do_one_iteration)
447 # Units for the timer are in milliseconds
447 # Units for the timer are in milliseconds
448 self.timer.start(1000*self._poll_interval)
448 self.timer.start(1000*self._poll_interval)
449 start_event_loop_qt4(self.app)
449 start_event_loop_qt4(self.app)
450
450
451
451
452 class WxKernel(Kernel):
452 class WxKernel(Kernel):
453 """A Kernel subclass with Wx support."""
453 """A Kernel subclass with Wx support."""
454
454
455 def start(self):
455 def start(self):
456 """Start a kernel with wx event loop support."""
456 """Start a kernel with wx event loop support."""
457
457
458 import wx
458 import wx
459 from IPython.lib.guisupport import start_event_loop_wx
459 from IPython.lib.guisupport import start_event_loop_wx
460
460
461 doi = self.do_one_iteration
461 doi = self.do_one_iteration
462 # Wx uses milliseconds
462 # Wx uses milliseconds
463 poll_interval = int(1000*self._poll_interval)
463 poll_interval = int(1000*self._poll_interval)
464
464
465 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
465 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
466 # We make the Frame hidden when we create it in the main app below.
466 # We make the Frame hidden when we create it in the main app below.
467 class TimerFrame(wx.Frame):
467 class TimerFrame(wx.Frame):
468 def __init__(self, func):
468 def __init__(self, func):
469 wx.Frame.__init__(self, None, -1)
469 wx.Frame.__init__(self, None, -1)
470 self.timer = wx.Timer(self)
470 self.timer = wx.Timer(self)
471 # Units for the timer are in milliseconds
471 # Units for the timer are in milliseconds
472 self.timer.Start(poll_interval)
472 self.timer.Start(poll_interval)
473 self.Bind(wx.EVT_TIMER, self.on_timer)
473 self.Bind(wx.EVT_TIMER, self.on_timer)
474 self.func = func
474 self.func = func
475
475
476 def on_timer(self, event):
476 def on_timer(self, event):
477 self.func()
477 self.func()
478
478
479 # We need a custom wx.App to create our Frame subclass that has the
479 # We need a custom wx.App to create our Frame subclass that has the
480 # wx.Timer to drive the ZMQ event loop.
480 # wx.Timer to drive the ZMQ event loop.
481 class IPWxApp(wx.App):
481 class IPWxApp(wx.App):
482 def OnInit(self):
482 def OnInit(self):
483 self.frame = TimerFrame(doi)
483 self.frame = TimerFrame(doi)
484 self.frame.Show(False)
484 self.frame.Show(False)
485 return True
485 return True
486
486
487 # The redirect=False here makes sure that wx doesn't replace
487 # The redirect=False here makes sure that wx doesn't replace
488 # sys.stdout/stderr with its own classes.
488 # sys.stdout/stderr with its own classes.
489 self.app = IPWxApp(redirect=False)
489 self.app = IPWxApp(redirect=False)
490 start_event_loop_wx(self.app)
490 start_event_loop_wx(self.app)
491
491
492
492
493 class TkKernel(Kernel):
493 class TkKernel(Kernel):
494 """A Kernel subclass with Tk support."""
494 """A Kernel subclass with Tk support."""
495
495
496 def start(self):
496 def start(self):
497 """Start a Tk enabled event loop."""
497 """Start a Tk enabled event loop."""
498
498
499 import Tkinter
499 import Tkinter
500 doi = self.do_one_iteration
500 doi = self.do_one_iteration
501 # Tk uses milliseconds
501 # Tk uses milliseconds
502 poll_interval = int(1000*self._poll_interval)
502 poll_interval = int(1000*self._poll_interval)
503 # For Tkinter, we create a Tk object and call its withdraw method.
503 # For Tkinter, we create a Tk object and call its withdraw method.
504 class Timer(object):
504 class Timer(object):
505 def __init__(self, func):
505 def __init__(self, func):
506 self.app = Tkinter.Tk()
506 self.app = Tkinter.Tk()
507 self.app.withdraw()
507 self.app.withdraw()
508 self.func = func
508 self.func = func
509
509
510 def on_timer(self):
510 def on_timer(self):
511 self.func()
511 self.func()
512 self.app.after(poll_interval, self.on_timer)
512 self.app.after(poll_interval, self.on_timer)
513
513
514 def start(self):
514 def start(self):
515 self.on_timer() # Call it once to get things going.
515 self.on_timer() # Call it once to get things going.
516 self.app.mainloop()
516 self.app.mainloop()
517
517
518 self.timer = Timer(doi)
518 self.timer = Timer(doi)
519 self.timer.start()
519 self.timer.start()
520
520
521
521
522 class GTKKernel(Kernel):
522 class GTKKernel(Kernel):
523 """A Kernel subclass with GTK support."""
523 """A Kernel subclass with GTK support."""
524
524
525 def start(self):
525 def start(self):
526 """Start the kernel, coordinating with the GTK event loop"""
526 """Start the kernel, coordinating with the GTK event loop"""
527 from .gui.gtkembed import GTKEmbed
527 from .gui.gtkembed import GTKEmbed
528
528
529 gtk_kernel = GTKEmbed(self)
529 gtk_kernel = GTKEmbed(self)
530 gtk_kernel.start()
530 gtk_kernel.start()
531
531
532
532
533 #-----------------------------------------------------------------------------
533 #-----------------------------------------------------------------------------
534 # Kernel main and launch functions
534 # Kernel main and launch functions
535 #-----------------------------------------------------------------------------
535 #-----------------------------------------------------------------------------
536
536
537 def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
537 def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
538 independent=False, pylab=False):
538 independent=False, pylab=False, colors=None):
539 """Launches a localhost kernel, binding to the specified ports.
539 """Launches a localhost kernel, binding to the specified ports.
540
540
541 Parameters
541 Parameters
542 ----------
542 ----------
543 ip : str, optional
543 ip : str, optional
544 The ip address the kernel will bind to.
544 The ip address the kernel will bind to.
545
545
546 xrep_port : int, optional
546 xrep_port : int, optional
547 The port to use for XREP channel.
547 The port to use for XREP channel.
548
548
549 pub_port : int, optional
549 pub_port : int, optional
550 The port to use for the SUB channel.
550 The port to use for the SUB channel.
551
551
552 req_port : int, optional
552 req_port : int, optional
553 The port to use for the REQ (raw input) channel.
553 The port to use for the REQ (raw input) channel.
554
554
555 hb_port : int, optional
555 hb_port : int, optional
556 The port to use for the hearbeat REP channel.
556 The port to use for the hearbeat REP channel.
557
557
558 independent : bool, optional (default False)
558 independent : bool, optional (default False)
559 If set, the kernel process is guaranteed to survive if this process
559 If set, the kernel process is guaranteed to survive if this process
560 dies. If not set, an effort is made to ensure that the kernel is killed
560 dies. If not set, an effort is made to ensure that the kernel is killed
561 when this process dies. Note that in this case it is still good practice
561 when this process dies. Note that in this case it is still good practice
562 to kill kernels manually before exiting.
562 to kill kernels manually before exiting.
563
563
564 pylab : bool or string, optional (default False)
564 pylab : bool or string, optional (default False)
565 If not False, the kernel will be launched with pylab enabled. If a
565 If not False, the kernel will be launched with pylab enabled. If a
566 string is passed, matplotlib will use the specified backend. Otherwise,
566 string is passed, matplotlib will use the specified backend. Otherwise,
567 matplotlib's default backend will be used.
567 matplotlib's default backend will be used.
568
568
569 colors : None or string, optional (default None)
570 If not None, specify the color scheme. One of (NoColor, LightBG, Linux)
571
569 Returns
572 Returns
570 -------
573 -------
571 A tuple of form:
574 A tuple of form:
572 (kernel_process, xrep_port, pub_port, req_port)
575 (kernel_process, xrep_port, pub_port, req_port)
573 where kernel_process is a Popen object and the ports are integers.
576 where kernel_process is a Popen object and the ports are integers.
574 """
577 """
575 extra_arguments = []
578 extra_arguments = []
576 if pylab:
579 if pylab:
577 extra_arguments.append('--pylab')
580 extra_arguments.append('--pylab')
578 if isinstance(pylab, basestring):
581 if isinstance(pylab, basestring):
579 extra_arguments.append(pylab)
582 extra_arguments.append(pylab)
580 if ip is not None:
583 if ip is not None:
581 extra_arguments.append('--ip')
584 extra_arguments.append('--ip')
582 if isinstance(ip, basestring):
585 if isinstance(ip, basestring):
583 extra_arguments.append(ip)
586 extra_arguments.append(ip)
587 if colors is not None:
588 extra_arguments.append('--colors')
589 extra_arguments.append(colors)
584 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
590 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
585 xrep_port, pub_port, req_port, hb_port,
591 xrep_port, pub_port, req_port, hb_port,
586 independent, extra_arguments)
592 independent, extra_arguments)
587
593
588
594
589 def main():
595 def main():
590 """ The IPython kernel main entry point.
596 """ The IPython kernel main entry point.
591 """
597 """
592 parser = make_argument_parser()
598 parser = make_argument_parser()
593 parser.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
599 parser.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
594 const='auto', help = \
600 const='auto', help = \
595 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
601 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
596 given, the GUI backend is matplotlib's, otherwise use one of: \
602 given, the GUI backend is matplotlib's, otherwise use one of: \
597 ['tk', 'gtk', 'qt', 'wx', 'inline'].")
603 ['tk', 'gtk', 'qt', 'wx', 'inline'].")
604 parser.add_argument('--colors',
605 type=str, dest='colors',
606 help="Set the color scheme (NoColor, Linux, and LightBG).",
607 metavar='ZMQInteractiveShell.colors')
598 namespace = parser.parse_args()
608 namespace = parser.parse_args()
599
609
600 kernel_class = Kernel
610 kernel_class = Kernel
601
611
602 kernel_classes = {
612 kernel_classes = {
603 'qt' : QtKernel,
613 'qt' : QtKernel,
604 'qt4': QtKernel,
614 'qt4': QtKernel,
605 'inline': Kernel,
615 'inline': Kernel,
606 'wx' : WxKernel,
616 'wx' : WxKernel,
607 'tk' : TkKernel,
617 'tk' : TkKernel,
608 'gtk': GTKKernel,
618 'gtk': GTKKernel,
609 }
619 }
610 if namespace.pylab:
620 if namespace.pylab:
611 if namespace.pylab == 'auto':
621 if namespace.pylab == 'auto':
612 gui, backend = pylabtools.find_gui_and_backend()
622 gui, backend = pylabtools.find_gui_and_backend()
613 else:
623 else:
614 gui, backend = pylabtools.find_gui_and_backend(namespace.pylab)
624 gui, backend = pylabtools.find_gui_and_backend(namespace.pylab)
615 kernel_class = kernel_classes.get(gui)
625 kernel_class = kernel_classes.get(gui)
616 if kernel_class is None:
626 if kernel_class is None:
617 raise ValueError('GUI is not supported: %r' % gui)
627 raise ValueError('GUI is not supported: %r' % gui)
618 pylabtools.activate_matplotlib(backend)
628 pylabtools.activate_matplotlib(backend)
629 if namespace.colors:
630 ZMQInteractiveShell.colors=namespace.colors
619
631
620 kernel = make_kernel(namespace, kernel_class, OutStream)
632 kernel = make_kernel(namespace, kernel_class, OutStream)
621
633
622 if namespace.pylab:
634 if namespace.pylab:
623 pylabtools.import_pylab(kernel.shell.user_ns, backend,
635 pylabtools.import_pylab(kernel.shell.user_ns, backend,
624 shell=kernel.shell)
636 shell=kernel.shell)
625
637
626 start_kernel(namespace, kernel)
638 start_kernel(namespace, kernel)
627
639
628
640
629 if __name__ == '__main__':
641 if __name__ == '__main__':
630 main()
642 main()
General Comments 0
You need to be logged in to leave comments. Login now