##// END OF EJS Templates
Implemented %loadpy magic for loading .py scripts into Qt console.
Brian Granger -
Show More
@@ -1,459 +1,473 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
18
18 # System library imports
19 # System library imports
19 from PyQt4 import QtCore, QtGui
20 from PyQt4 import QtCore, QtGui
20
21
21 # Local imports
22 # Local imports
22 from IPython.core.inputsplitter import IPythonInputSplitter, \
23 from IPython.core.inputsplitter import IPythonInputSplitter, \
23 transform_ipy_prompt
24 transform_ipy_prompt
24 from IPython.core.usage import default_gui_banner
25 from IPython.core.usage import default_gui_banner
25 from IPython.utils.traitlets import Bool, Str
26 from IPython.utils.traitlets import Bool, Str
26 from frontend_widget import FrontendWidget
27 from frontend_widget import FrontendWidget
27
28
28 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
29 # Constants
30 # Constants
30 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
31
32
32 # The default light style sheet: black text on a white background.
33 # The default light style sheet: black text on a white background.
33 default_light_style_sheet = '''
34 default_light_style_sheet = '''
34 .error { color: red; }
35 .error { color: red; }
35 .in-prompt { color: navy; }
36 .in-prompt { color: navy; }
36 .in-prompt-number { font-weight: bold; }
37 .in-prompt-number { font-weight: bold; }
37 .out-prompt { color: darkred; }
38 .out-prompt { color: darkred; }
38 .out-prompt-number { font-weight: bold; }
39 .out-prompt-number { font-weight: bold; }
39 '''
40 '''
40 default_light_syntax_style = 'default'
41 default_light_syntax_style = 'default'
41
42
42 # The default dark style sheet: white text on a black background.
43 # The default dark style sheet: white text on a black background.
43 default_dark_style_sheet = '''
44 default_dark_style_sheet = '''
44 QPlainTextEdit, QTextEdit { background-color: black; color: white }
45 QPlainTextEdit, QTextEdit { background-color: black; color: white }
45 QFrame { border: 1px solid grey; }
46 QFrame { border: 1px solid grey; }
46 .error { color: red; }
47 .error { color: red; }
47 .in-prompt { color: lime; }
48 .in-prompt { color: lime; }
48 .in-prompt-number { color: lime; font-weight: bold; }
49 .in-prompt-number { color: lime; font-weight: bold; }
49 .out-prompt { color: red; }
50 .out-prompt { color: red; }
50 .out-prompt-number { color: red; font-weight: bold; }
51 .out-prompt-number { color: red; font-weight: bold; }
51 '''
52 '''
52 default_dark_syntax_style = 'monokai'
53 default_dark_syntax_style = 'monokai'
53
54
54 # Default strings to build and display input and output prompts (and separators
55 # Default strings to build and display input and output prompts (and separators
55 # in between)
56 # in between)
56 default_in_prompt = 'In [<span class="in-prompt-number">%i</span>]: '
57 default_in_prompt = 'In [<span class="in-prompt-number">%i</span>]: '
57 default_out_prompt = 'Out[<span class="out-prompt-number">%i</span>]: '
58 default_out_prompt = 'Out[<span class="out-prompt-number">%i</span>]: '
58 default_input_sep = '\n'
59 default_input_sep = '\n'
59 default_output_sep = ''
60 default_output_sep = ''
60 default_output_sep2 = ''
61 default_output_sep2 = ''
61
62
62 #-----------------------------------------------------------------------------
63 #-----------------------------------------------------------------------------
63 # IPythonWidget class
64 # IPythonWidget class
64 #-----------------------------------------------------------------------------
65 #-----------------------------------------------------------------------------
65
66
66 class IPythonWidget(FrontendWidget):
67 class IPythonWidget(FrontendWidget):
67 """ A FrontendWidget for an IPython kernel.
68 """ A FrontendWidget for an IPython kernel.
68 """
69 """
69
70
70 # If set, the 'custom_edit_requested(str, int)' signal will be emitted when
71 # If set, the 'custom_edit_requested(str, int)' signal will be emitted when
71 # an editor is needed for a file. This overrides 'editor' and 'editor_line'
72 # an editor is needed for a file. This overrides 'editor' and 'editor_line'
72 # settings.
73 # settings.
73 custom_edit = Bool(False)
74 custom_edit = Bool(False)
74 custom_edit_requested = QtCore.pyqtSignal(object, object)
75 custom_edit_requested = QtCore.pyqtSignal(object, object)
75
76
76 # A command for invoking a system text editor. If the string contains a
77 # A command for invoking a system text editor. If the string contains a
77 # {filename} format specifier, it will be used. Otherwise, the filename will
78 # {filename} format specifier, it will be used. Otherwise, the filename will
78 # be appended to the end the command.
79 # be appended to the end the command.
79 editor = Str('default', config=True)
80 editor = Str('default', config=True)
80
81
81 # The editor command to use when a specific line number is requested. The
82 # The editor command to use when a specific line number is requested. The
82 # string should contain two format specifiers: {line} and {filename}. If
83 # string should contain two format specifiers: {line} and {filename}. If
83 # this parameter is not specified, the line number option to the %edit magic
84 # this parameter is not specified, the line number option to the %edit magic
84 # will be ignored.
85 # will be ignored.
85 editor_line = Str(config=True)
86 editor_line = Str(config=True)
86
87
87 # A CSS stylesheet. The stylesheet can contain classes for:
88 # A CSS stylesheet. The stylesheet can contain classes for:
88 # 1. Qt: QPlainTextEdit, QFrame, QWidget, etc
89 # 1. Qt: QPlainTextEdit, QFrame, QWidget, etc
89 # 2. Pygments: .c, .k, .o, etc (see PygmentsHighlighter)
90 # 2. Pygments: .c, .k, .o, etc (see PygmentsHighlighter)
90 # 3. IPython: .error, .in-prompt, .out-prompt, etc
91 # 3. IPython: .error, .in-prompt, .out-prompt, etc
91 style_sheet = Str(config=True)
92 style_sheet = Str(config=True)
92
93
93 # If not empty, use this Pygments style for syntax highlighting. Otherwise,
94 # If not empty, use this Pygments style for syntax highlighting. Otherwise,
94 # the style sheet is queried for Pygments style information.
95 # the style sheet is queried for Pygments style information.
95 syntax_style = Str(config=True)
96 syntax_style = Str(config=True)
96
97
97 # Prompts.
98 # Prompts.
98 in_prompt = Str(default_in_prompt, config=True)
99 in_prompt = Str(default_in_prompt, config=True)
99 out_prompt = Str(default_out_prompt, config=True)
100 out_prompt = Str(default_out_prompt, config=True)
100 input_sep = Str(default_input_sep, config=True)
101 input_sep = Str(default_input_sep, config=True)
101 output_sep = Str(default_output_sep, config=True)
102 output_sep = Str(default_output_sep, config=True)
102 output_sep2 = Str(default_output_sep2, config=True)
103 output_sep2 = Str(default_output_sep2, config=True)
103
104
104 # FrontendWidget protected class variables.
105 # FrontendWidget protected class variables.
105 _input_splitter_class = IPythonInputSplitter
106 _input_splitter_class = IPythonInputSplitter
106
107
107 # IPythonWidget protected class variables.
108 # IPythonWidget protected class variables.
108 _PromptBlock = namedtuple('_PromptBlock', ['block', 'length', 'number'])
109 _PromptBlock = namedtuple('_PromptBlock', ['block', 'length', 'number'])
109 _payload_source_edit = 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic'
110 _payload_source_edit = 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic'
110 _payload_source_exit = 'IPython.zmq.zmqshell.ZMQInteractiveShell.ask_exit'
111 _payload_source_exit = 'IPython.zmq.zmqshell.ZMQInteractiveShell.ask_exit'
111 _payload_source_page = 'IPython.zmq.page.page'
112 _payload_source_page = 'IPython.zmq.page.page'
113 _payload_source_loadpy = 'IPython.zmq.zmqshell.ZMQInteractiveShell.magic_loadpy'
112
114
113 #---------------------------------------------------------------------------
115 #---------------------------------------------------------------------------
114 # 'object' interface
116 # 'object' interface
115 #---------------------------------------------------------------------------
117 #---------------------------------------------------------------------------
116
118
117 def __init__(self, *args, **kw):
119 def __init__(self, *args, **kw):
118 super(IPythonWidget, self).__init__(*args, **kw)
120 super(IPythonWidget, self).__init__(*args, **kw)
119
121
120 # IPythonWidget protected variables.
122 # IPythonWidget protected variables.
121 self._payload_handlers = {
123 self._payload_handlers = {
122 self._payload_source_edit : self._handle_payload_edit,
124 self._payload_source_edit : self._handle_payload_edit,
123 self._payload_source_exit : self._handle_payload_exit,
125 self._payload_source_exit : self._handle_payload_exit,
124 self._payload_source_page : self._handle_payload_page }
126 self._payload_source_page : self._handle_payload_page,
127 self._payload_source_loadpy : self._handle_payload_loadpy }
125 self._previous_prompt_obj = None
128 self._previous_prompt_obj = None
129 self._code_to_load = None
126
130
127 # Initialize widget styling.
131 # Initialize widget styling.
128 if self.style_sheet:
132 if self.style_sheet:
129 self._style_sheet_changed()
133 self._style_sheet_changed()
130 self._syntax_style_changed()
134 self._syntax_style_changed()
131 else:
135 else:
132 self.set_default_style()
136 self.set_default_style()
133
137
134 #---------------------------------------------------------------------------
138 #---------------------------------------------------------------------------
135 # 'BaseFrontendMixin' abstract interface
139 # 'BaseFrontendMixin' abstract interface
136 #---------------------------------------------------------------------------
140 #---------------------------------------------------------------------------
137
141
138 def _handle_complete_reply(self, rep):
142 def _handle_complete_reply(self, rep):
139 """ Reimplemented to support IPython's improved completion machinery.
143 """ Reimplemented to support IPython's improved completion machinery.
140 """
144 """
141 cursor = self._get_cursor()
145 cursor = self._get_cursor()
142 info = self._request_info.get('complete')
146 info = self._request_info.get('complete')
143 if info and info.id == rep['parent_header']['msg_id'] and \
147 if info and info.id == rep['parent_header']['msg_id'] and \
144 info.pos == cursor.position():
148 info.pos == cursor.position():
145 matches = rep['content']['matches']
149 matches = rep['content']['matches']
146 text = rep['content']['matched_text']
150 text = rep['content']['matched_text']
147 offset = len(text)
151 offset = len(text)
148
152
149 # Clean up matches with period and path separators if the matched
153 # Clean up matches with period and path separators if the matched
150 # text has not been transformed. This is done by truncating all
154 # text has not been transformed. This is done by truncating all
151 # but the last component and then suitably decreasing the offset
155 # but the last component and then suitably decreasing the offset
152 # between the current cursor position and the start of completion.
156 # between the current cursor position and the start of completion.
153 if len(matches) > 1 and matches[0][:offset] == text:
157 if len(matches) > 1 and matches[0][:offset] == text:
154 parts = re.split(r'[./\\]', text)
158 parts = re.split(r'[./\\]', text)
155 sep_count = len(parts) - 1
159 sep_count = len(parts) - 1
156 if sep_count:
160 if sep_count:
157 chop_length = sum(map(len, parts[:sep_count])) + sep_count
161 chop_length = sum(map(len, parts[:sep_count])) + sep_count
158 matches = [ match[chop_length:] for match in matches ]
162 matches = [ match[chop_length:] for match in matches ]
159 offset -= chop_length
163 offset -= chop_length
160
164
161 # Move the cursor to the start of the match and complete.
165 # Move the cursor to the start of the match and complete.
162 cursor.movePosition(QtGui.QTextCursor.Left, n=offset)
166 cursor.movePosition(QtGui.QTextCursor.Left, n=offset)
163 self._complete_with_items(cursor, matches)
167 self._complete_with_items(cursor, matches)
164
168
165 def _handle_execute_reply(self, msg):
169 def _handle_execute_reply(self, msg):
166 """ Reimplemented to support prompt requests.
170 """ Reimplemented to support prompt requests.
167 """
171 """
168 info = self._request_info.get('execute')
172 info = self._request_info.get('execute')
169 if info and info.id == msg['parent_header']['msg_id']:
173 if info and info.id == msg['parent_header']['msg_id']:
170 if info.kind == 'prompt':
174 if info.kind == 'prompt':
171 number = msg['content']['execution_count'] + 1
175 number = msg['content']['execution_count'] + 1
172 self._show_interpreter_prompt(number)
176 self._show_interpreter_prompt(number)
173 else:
177 else:
174 super(IPythonWidget, self)._handle_execute_reply(msg)
178 super(IPythonWidget, self)._handle_execute_reply(msg)
175
179
176 def _handle_history_reply(self, msg):
180 def _handle_history_reply(self, msg):
177 """ Implemented to handle history replies, which are only supported by
181 """ Implemented to handle history replies, which are only supported by
178 the IPython kernel.
182 the IPython kernel.
179 """
183 """
180 history_dict = msg['content']['history']
184 history_dict = msg['content']['history']
181 items = [ history_dict[key] for key in sorted(history_dict.keys()) ]
185 items = [ history_dict[key] for key in sorted(history_dict.keys()) ]
182 self._set_history(items)
186 self._set_history(items)
183
187
184 def _handle_pyout(self, msg):
188 def _handle_pyout(self, msg):
185 """ Reimplemented for IPython-style "display hook".
189 """ Reimplemented for IPython-style "display hook".
186 """
190 """
187 if not self._hidden and self._is_from_this_session(msg):
191 if not self._hidden and self._is_from_this_session(msg):
188 content = msg['content']
192 content = msg['content']
189 prompt_number = content['execution_count']
193 prompt_number = content['execution_count']
190 self._append_plain_text(self.output_sep)
194 self._append_plain_text(self.output_sep)
191 self._append_html(self._make_out_prompt(prompt_number))
195 self._append_html(self._make_out_prompt(prompt_number))
192 self._append_plain_text(content['data']+self.output_sep2)
196 self._append_plain_text(content['data']+self.output_sep2)
193
197
194 def _started_channels(self):
198 def _started_channels(self):
195 """ Reimplemented to make a history request.
199 """ Reimplemented to make a history request.
196 """
200 """
197 super(IPythonWidget, self)._started_channels()
201 super(IPythonWidget, self)._started_channels()
198 # FIXME: Disabled until history requests are properly implemented.
202 # FIXME: Disabled until history requests are properly implemented.
199 #self.kernel_manager.xreq_channel.history(raw=True, output=False)
203 #self.kernel_manager.xreq_channel.history(raw=True, output=False)
200
204
201 #---------------------------------------------------------------------------
205 #---------------------------------------------------------------------------
202 # 'ConsoleWidget' public interface
206 # 'ConsoleWidget' public interface
203 #---------------------------------------------------------------------------
207 #---------------------------------------------------------------------------
204
208
205 def copy(self):
209 def copy(self):
206 """ Copy the currently selected text to the clipboard, removing prompts
210 """ Copy the currently selected text to the clipboard, removing prompts
207 if possible.
211 if possible.
208 """
212 """
209 text = unicode(self._control.textCursor().selection().toPlainText())
213 text = unicode(self._control.textCursor().selection().toPlainText())
210 if text:
214 if text:
211 lines = map(transform_ipy_prompt, text.splitlines())
215 lines = map(transform_ipy_prompt, text.splitlines())
212 text = '\n'.join(lines)
216 text = '\n'.join(lines)
213 QtGui.QApplication.clipboard().setText(text)
217 QtGui.QApplication.clipboard().setText(text)
214
218
215 #---------------------------------------------------------------------------
219 #---------------------------------------------------------------------------
216 # 'FrontendWidget' public interface
220 # 'FrontendWidget' public interface
217 #---------------------------------------------------------------------------
221 #---------------------------------------------------------------------------
218
222
219 def execute_file(self, path, hidden=False):
223 def execute_file(self, path, hidden=False):
220 """ Reimplemented to use the 'run' magic.
224 """ Reimplemented to use the 'run' magic.
221 """
225 """
222 self.execute('%%run %s' % path, hidden=hidden)
226 self.execute('%%run %s' % path, hidden=hidden)
223
227
224 #---------------------------------------------------------------------------
228 #---------------------------------------------------------------------------
225 # 'FrontendWidget' protected interface
229 # 'FrontendWidget' protected interface
226 #---------------------------------------------------------------------------
230 #---------------------------------------------------------------------------
227
231
228 def _complete(self):
232 def _complete(self):
229 """ Reimplemented to support IPython's improved completion machinery.
233 """ Reimplemented to support IPython's improved completion machinery.
230 """
234 """
231 # We let the kernel split the input line, so we *always* send an empty
235 # We let the kernel split the input line, so we *always* send an empty
232 # text field. Readline-based frontends do get a real text field which
236 # text field. Readline-based frontends do get a real text field which
233 # they can use.
237 # they can use.
234 text = ''
238 text = ''
235
239
236 # Send the completion request to the kernel
240 # Send the completion request to the kernel
237 msg_id = self.kernel_manager.xreq_channel.complete(
241 msg_id = self.kernel_manager.xreq_channel.complete(
238 text, # text
242 text, # text
239 self._get_input_buffer_cursor_line(), # line
243 self._get_input_buffer_cursor_line(), # line
240 self._get_input_buffer_cursor_column(), # cursor_pos
244 self._get_input_buffer_cursor_column(), # cursor_pos
241 self.input_buffer) # block
245 self.input_buffer) # block
242 pos = self._get_cursor().position()
246 pos = self._get_cursor().position()
243 info = self._CompletionRequest(msg_id, pos)
247 info = self._CompletionRequest(msg_id, pos)
244 self._request_info['complete'] = info
248 self._request_info['complete'] = info
245
249
246 def _get_banner(self):
250 def _get_banner(self):
247 """ Reimplemented to return IPython's default banner.
251 """ Reimplemented to return IPython's default banner.
248 """
252 """
249 return default_gui_banner
253 return default_gui_banner
250
254
251 def _process_execute_error(self, msg):
255 def _process_execute_error(self, msg):
252 """ Reimplemented for IPython-style traceback formatting.
256 """ Reimplemented for IPython-style traceback formatting.
253 """
257 """
254 content = msg['content']
258 content = msg['content']
255 traceback = '\n'.join(content['traceback']) + '\n'
259 traceback = '\n'.join(content['traceback']) + '\n'
256 if False:
260 if False:
257 # FIXME: For now, tracebacks come as plain text, so we can't use
261 # FIXME: For now, tracebacks come as plain text, so we can't use
258 # the html renderer yet. Once we refactor ultratb to produce
262 # the html renderer yet. Once we refactor ultratb to produce
259 # properly styled tracebacks, this branch should be the default
263 # properly styled tracebacks, this branch should be the default
260 traceback = traceback.replace(' ', '&nbsp;')
264 traceback = traceback.replace(' ', '&nbsp;')
261 traceback = traceback.replace('\n', '<br/>')
265 traceback = traceback.replace('\n', '<br/>')
262
266
263 ename = content['ename']
267 ename = content['ename']
264 ename_styled = '<span class="error">%s</span>' % ename
268 ename_styled = '<span class="error">%s</span>' % ename
265 traceback = traceback.replace(ename, ename_styled)
269 traceback = traceback.replace(ename, ename_styled)
266
270
267 self._append_html(traceback)
271 self._append_html(traceback)
268 else:
272 else:
269 # This is the fallback for now, using plain text with ansi escapes
273 # This is the fallback for now, using plain text with ansi escapes
270 self._append_plain_text(traceback)
274 self._append_plain_text(traceback)
271
275
272 def _process_execute_payload(self, item):
276 def _process_execute_payload(self, item):
273 """ Reimplemented to dispatch payloads to handler methods.
277 """ Reimplemented to dispatch payloads to handler methods.
274 """
278 """
275 handler = self._payload_handlers.get(item['source'])
279 handler = self._payload_handlers.get(item['source'])
276 if handler is None:
280 if handler is None:
277 # We have no handler for this type of payload, simply ignore it
281 # We have no handler for this type of payload, simply ignore it
278 return False
282 return False
279 else:
283 else:
280 handler(item)
284 handler(item)
281 return True
285 return True
282
286
283 def _show_interpreter_prompt(self, number=None):
287 def _show_interpreter_prompt(self, number=None):
284 """ Reimplemented for IPython-style prompts.
288 """ Reimplemented for IPython-style prompts.
285 """
289 """
286 # If a number was not specified, make a prompt number request.
290 # If a number was not specified, make a prompt number request.
287 if number is None:
291 if number is None:
288 msg_id = self.kernel_manager.xreq_channel.execute('', silent=True)
292 msg_id = self.kernel_manager.xreq_channel.execute('', silent=True)
289 info = self._ExecutionRequest(msg_id, 'prompt')
293 info = self._ExecutionRequest(msg_id, 'prompt')
290 self._request_info['execute'] = info
294 self._request_info['execute'] = info
291 return
295 return
292
296
293 # Show a new prompt and save information about it so that it can be
297 # Show a new prompt and save information about it so that it can be
294 # updated later if the prompt number turns out to be wrong.
298 # updated later if the prompt number turns out to be wrong.
295 self._prompt_sep = self.input_sep
299 self._prompt_sep = self.input_sep
296 self._show_prompt(self._make_in_prompt(number), html=True)
300 self._show_prompt(self._make_in_prompt(number), html=True)
297 block = self._control.document().lastBlock()
301 block = self._control.document().lastBlock()
298 length = len(self._prompt)
302 length = len(self._prompt)
299 self._previous_prompt_obj = self._PromptBlock(block, length, number)
303 self._previous_prompt_obj = self._PromptBlock(block, length, number)
300
304
301 # Update continuation prompt to reflect (possibly) new prompt length.
305 # Update continuation prompt to reflect (possibly) new prompt length.
302 self._set_continuation_prompt(
306 self._set_continuation_prompt(
303 self._make_continuation_prompt(self._prompt), html=True)
307 self._make_continuation_prompt(self._prompt), html=True)
304
308
309 if self._code_to_load is not None:
310 text = unicode(self._code_to_load).rstrip()
311 self._insert_plain_text_into_buffer(dedent(text))
312 self._code_to_load = None
313
305 def _show_interpreter_prompt_for_reply(self, msg):
314 def _show_interpreter_prompt_for_reply(self, msg):
306 """ Reimplemented for IPython-style prompts.
315 """ Reimplemented for IPython-style prompts.
307 """
316 """
308 # Update the old prompt number if necessary.
317 # Update the old prompt number if necessary.
309 content = msg['content']
318 content = msg['content']
310 previous_prompt_number = content['execution_count']
319 previous_prompt_number = content['execution_count']
311 if self._previous_prompt_obj and \
320 if self._previous_prompt_obj and \
312 self._previous_prompt_obj.number != previous_prompt_number:
321 self._previous_prompt_obj.number != previous_prompt_number:
313 block = self._previous_prompt_obj.block
322 block = self._previous_prompt_obj.block
314
323
315 # Make sure the prompt block has not been erased.
324 # Make sure the prompt block has not been erased.
316 if block.isValid() and not block.text().isEmpty():
325 if block.isValid() and not block.text().isEmpty():
317
326
318 # Remove the old prompt and insert a new prompt.
327 # Remove the old prompt and insert a new prompt.
319 cursor = QtGui.QTextCursor(block)
328 cursor = QtGui.QTextCursor(block)
320 cursor.movePosition(QtGui.QTextCursor.Right,
329 cursor.movePosition(QtGui.QTextCursor.Right,
321 QtGui.QTextCursor.KeepAnchor,
330 QtGui.QTextCursor.KeepAnchor,
322 self._previous_prompt_obj.length)
331 self._previous_prompt_obj.length)
323 prompt = self._make_in_prompt(previous_prompt_number)
332 prompt = self._make_in_prompt(previous_prompt_number)
324 self._prompt = self._insert_html_fetching_plain_text(
333 self._prompt = self._insert_html_fetching_plain_text(
325 cursor, prompt)
334 cursor, prompt)
326
335
327 # When the HTML is inserted, Qt blows away the syntax
336 # When the HTML is inserted, Qt blows away the syntax
328 # highlighting for the line, so we need to rehighlight it.
337 # highlighting for the line, so we need to rehighlight it.
329 self._highlighter.rehighlightBlock(cursor.block())
338 self._highlighter.rehighlightBlock(cursor.block())
330
339
331 self._previous_prompt_obj = None
340 self._previous_prompt_obj = None
332
341
333 # Show a new prompt with the kernel's estimated prompt number.
342 # Show a new prompt with the kernel's estimated prompt number.
334 self._show_interpreter_prompt(previous_prompt_number+1)
343 self._show_interpreter_prompt(previous_prompt_number+1)
335
344
336 #---------------------------------------------------------------------------
345 #---------------------------------------------------------------------------
337 # 'IPythonWidget' interface
346 # 'IPythonWidget' interface
338 #---------------------------------------------------------------------------
347 #---------------------------------------------------------------------------
339
348
340 def set_default_style(self, lightbg=True):
349 def set_default_style(self, lightbg=True):
341 """ Sets the widget style to the class defaults.
350 """ Sets the widget style to the class defaults.
342
351
343 Parameters:
352 Parameters:
344 -----------
353 -----------
345 lightbg : bool, optional (default True)
354 lightbg : bool, optional (default True)
346 Whether to use the default IPython light background or dark
355 Whether to use the default IPython light background or dark
347 background style.
356 background style.
348 """
357 """
349 if lightbg:
358 if lightbg:
350 self.style_sheet = default_light_style_sheet
359 self.style_sheet = default_light_style_sheet
351 self.syntax_style = default_light_syntax_style
360 self.syntax_style = default_light_syntax_style
352 else:
361 else:
353 self.style_sheet = default_dark_style_sheet
362 self.style_sheet = default_dark_style_sheet
354 self.syntax_style = default_dark_syntax_style
363 self.syntax_style = default_dark_syntax_style
355
364
356 #---------------------------------------------------------------------------
365 #---------------------------------------------------------------------------
357 # 'IPythonWidget' protected interface
366 # 'IPythonWidget' protected interface
358 #---------------------------------------------------------------------------
367 #---------------------------------------------------------------------------
359
368
360 def _edit(self, filename, line=None):
369 def _edit(self, filename, line=None):
361 """ Opens a Python script for editing.
370 """ Opens a Python script for editing.
362
371
363 Parameters:
372 Parameters:
364 -----------
373 -----------
365 filename : str
374 filename : str
366 A path to a local system file.
375 A path to a local system file.
367
376
368 line : int, optional
377 line : int, optional
369 A line of interest in the file.
378 A line of interest in the file.
370 """
379 """
371 if self.custom_edit:
380 if self.custom_edit:
372 self.custom_edit_requested.emit(filename, line)
381 self.custom_edit_requested.emit(filename, line)
373 elif self.editor == 'default':
382 elif self.editor == 'default':
374 self._append_plain_text('No default editor available.\n')
383 self._append_plain_text('No default editor available.\n')
375 else:
384 else:
376 try:
385 try:
377 filename = '"%s"' % filename
386 filename = '"%s"' % filename
378 if line and self.editor_line:
387 if line and self.editor_line:
379 command = self.editor_line.format(filename=filename,
388 command = self.editor_line.format(filename=filename,
380 line=line)
389 line=line)
381 else:
390 else:
382 try:
391 try:
383 command = self.editor.format()
392 command = self.editor.format()
384 except KeyError:
393 except KeyError:
385 command = self.editor.format(filename=filename)
394 command = self.editor.format(filename=filename)
386 else:
395 else:
387 command += ' ' + filename
396 command += ' ' + filename
388 except KeyError:
397 except KeyError:
389 self._append_plain_text('Invalid editor command.\n')
398 self._append_plain_text('Invalid editor command.\n')
390 else:
399 else:
391 try:
400 try:
392 Popen(command, shell=True)
401 Popen(command, shell=True)
393 except OSError:
402 except OSError:
394 msg = 'Opening editor with command "%s" failed.\n'
403 msg = 'Opening editor with command "%s" failed.\n'
395 self._append_plain_text(msg % command)
404 self._append_plain_text(msg % command)
396
405
397 def _make_in_prompt(self, number):
406 def _make_in_prompt(self, number):
398 """ Given a prompt number, returns an HTML In prompt.
407 """ Given a prompt number, returns an HTML In prompt.
399 """
408 """
400 body = self.in_prompt % number
409 body = self.in_prompt % number
401 return '<span class="in-prompt">%s</span>' % body
410 return '<span class="in-prompt">%s</span>' % body
402
411
403 def _make_continuation_prompt(self, prompt):
412 def _make_continuation_prompt(self, prompt):
404 """ Given a plain text version of an In prompt, returns an HTML
413 """ Given a plain text version of an In prompt, returns an HTML
405 continuation prompt.
414 continuation prompt.
406 """
415 """
407 end_chars = '...: '
416 end_chars = '...: '
408 space_count = len(prompt.lstrip('\n')) - len(end_chars)
417 space_count = len(prompt.lstrip('\n')) - len(end_chars)
409 body = '&nbsp;' * space_count + end_chars
418 body = '&nbsp;' * space_count + end_chars
410 return '<span class="in-prompt">%s</span>' % body
419 return '<span class="in-prompt">%s</span>' % body
411
420
412 def _make_out_prompt(self, number):
421 def _make_out_prompt(self, number):
413 """ Given a prompt number, returns an HTML Out prompt.
422 """ Given a prompt number, returns an HTML Out prompt.
414 """
423 """
415 body = self.out_prompt % number
424 body = self.out_prompt % number
416 return '<span class="out-prompt">%s</span>' % body
425 return '<span class="out-prompt">%s</span>' % body
417
426
418 #------ Payload handlers --------------------------------------------------
427 #------ Payload handlers --------------------------------------------------
419
428
420 # Payload handlers with a generic interface: each takes the opaque payload
429 # Payload handlers with a generic interface: each takes the opaque payload
421 # dict, unpacks it and calls the underlying functions with the necessary
430 # dict, unpacks it and calls the underlying functions with the necessary
422 # arguments.
431 # arguments.
423
432
424 def _handle_payload_edit(self, item):
433 def _handle_payload_edit(self, item):
425 self._edit(item['filename'], item['line_number'])
434 self._edit(item['filename'], item['line_number'])
426
435
427 def _handle_payload_exit(self, item):
436 def _handle_payload_exit(self, item):
428 self.exit_requested.emit()
437 self.exit_requested.emit()
429
438
430 def _handle_payload_page(self, item):
439 def _handle_payload_page(self, item):
431 # Since the plain text widget supports only a very small subset of HTML
440 # Since the plain text widget supports only a very small subset of HTML
432 # and we have no control over the HTML source, we only page HTML
441 # and we have no control over the HTML source, we only page HTML
433 # payloads in the rich text widget.
442 # payloads in the rich text widget.
434 if item['html'] and self.kind == 'rich':
443 if item['html'] and self.kind == 'rich':
435 self._page(item['html'], html=True)
444 self._page(item['html'], html=True)
436 else:
445 else:
437 self._page(item['text'], html=False)
446 self._page(item['text'], html=False)
438
447
448 def _handle_payload_loadpy(self, item):
449 # Simple save the text of the .py file for later. The text is written
450 # to the buffer when _prompt_started_hook is called.
451 self._code_to_load = item['text']
452
439 #------ Trait change handlers ---------------------------------------------
453 #------ Trait change handlers ---------------------------------------------
440
454
441 def _style_sheet_changed(self):
455 def _style_sheet_changed(self):
442 """ Set the style sheets of the underlying widgets.
456 """ Set the style sheets of the underlying widgets.
443 """
457 """
444 self.setStyleSheet(self.style_sheet)
458 self.setStyleSheet(self.style_sheet)
445 self._control.document().setDefaultStyleSheet(self.style_sheet)
459 self._control.document().setDefaultStyleSheet(self.style_sheet)
446 if self._page_control:
460 if self._page_control:
447 self._page_control.document().setDefaultStyleSheet(self.style_sheet)
461 self._page_control.document().setDefaultStyleSheet(self.style_sheet)
448
462
449 bg_color = self._control.palette().background().color()
463 bg_color = self._control.palette().background().color()
450 self._ansi_processor.set_background_color(bg_color)
464 self._ansi_processor.set_background_color(bg_color)
451
465
452 def _syntax_style_changed(self):
466 def _syntax_style_changed(self):
453 """ Set the style for the syntax highlighter.
467 """ Set the style for the syntax highlighter.
454 """
468 """
455 if self.syntax_style:
469 if self.syntax_style:
456 self._highlighter.set_style(self.syntax_style)
470 self._highlighter.set_style(self.syntax_style)
457 else:
471 else:
458 self._highlighter.set_style_sheet(self.style_sheet)
472 self._highlighter.set_style_sheet(self.style_sheet)
459
473
@@ -1,546 +1,567 b''
1 """A ZMQ-based subclass of InteractiveShell.
1 """A ZMQ-based subclass of InteractiveShell.
2
2
3 This code is meant to ease the refactoring of the base InteractiveShell into
3 This code is meant to ease the refactoring of the base InteractiveShell into
4 something with a cleaner architecture for 2-process use, without actually
4 something with a cleaner architecture for 2-process use, without actually
5 breaking InteractiveShell itself. So we're doing something a bit ugly, where
5 breaking InteractiveShell itself. So we're doing something a bit ugly, where
6 we subclass and override what we want to fix. Once this is working well, we
6 we subclass and override what we want to fix. Once this is working well, we
7 can go back to the base class and refactor the code for a cleaner inheritance
7 can go back to the base class and refactor the code for a cleaner inheritance
8 implementation that doesn't rely on so much monkeypatching.
8 implementation that doesn't rely on so much monkeypatching.
9
9
10 But this lets us maintain a fully working IPython as we develop the new
10 But this lets us maintain a fully working IPython as we develop the new
11 machinery. This should thus be thought of as scaffolding.
11 machinery. This should thus be thought of as scaffolding.
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 # Stdlib
18 # Stdlib
19 import inspect
19 import inspect
20 import os
20 import os
21 import re
21 import re
22
22
23 # Our own
23 # Our own
24 from IPython.core.interactiveshell import (
24 from IPython.core.interactiveshell import (
25 InteractiveShell, InteractiveShellABC
25 InteractiveShell, InteractiveShellABC
26 )
26 )
27 from IPython.core import page
27 from IPython.core import page
28 from IPython.core.displayhook import DisplayHook
28 from IPython.core.displayhook import DisplayHook
29 from IPython.core.macro import Macro
29 from IPython.core.macro import Macro
30 from IPython.core.payloadpage import install_payload_page
30 from IPython.core.payloadpage import install_payload_page
31 from IPython.utils import io
31 from IPython.utils import io
32 from IPython.utils.path import get_py_filename
32 from IPython.utils.path import get_py_filename
33 from IPython.utils.text import StringTypes
33 from IPython.utils.text import StringTypes
34 from IPython.utils.traitlets import Instance, Type, Dict
34 from IPython.utils.traitlets import Instance, Type, Dict
35 from IPython.utils.warn import warn
35 from IPython.utils.warn import warn
36 from IPython.zmq.session import extract_header
36 from IPython.zmq.session import extract_header
37 from session import Session
37 from session import Session
38
38
39 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
40 # Globals and side-effects
40 # Globals and side-effects
41 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
42
42
43 # Install the payload version of page.
43 # Install the payload version of page.
44 install_payload_page()
44 install_payload_page()
45
45
46 #-----------------------------------------------------------------------------
46 #-----------------------------------------------------------------------------
47 # Functions and classes
47 # Functions and classes
48 #-----------------------------------------------------------------------------
48 #-----------------------------------------------------------------------------
49
49
50 class ZMQDisplayHook(DisplayHook):
50 class ZMQDisplayHook(DisplayHook):
51
51
52 session = Instance(Session)
52 session = Instance(Session)
53 pub_socket = Instance('zmq.Socket')
53 pub_socket = Instance('zmq.Socket')
54 parent_header = Dict({})
54 parent_header = Dict({})
55
55
56 def set_parent(self, parent):
56 def set_parent(self, parent):
57 """Set the parent for outbound messages."""
57 """Set the parent for outbound messages."""
58 self.parent_header = extract_header(parent)
58 self.parent_header = extract_header(parent)
59
59
60 def start_displayhook(self):
60 def start_displayhook(self):
61 self.msg = self.session.msg(u'pyout', {}, parent=self.parent_header)
61 self.msg = self.session.msg(u'pyout', {}, parent=self.parent_header)
62
62
63 def write_output_prompt(self):
63 def write_output_prompt(self):
64 """Write the output prompt."""
64 """Write the output prompt."""
65 if self.do_full_cache:
65 if self.do_full_cache:
66 self.msg['content']['execution_count'] = self.prompt_count
66 self.msg['content']['execution_count'] = self.prompt_count
67
67
68 def write_result_repr(self, result_repr):
68 def write_result_repr(self, result_repr):
69 self.msg['content']['data'] = result_repr
69 self.msg['content']['data'] = result_repr
70
70
71 def finish_displayhook(self):
71 def finish_displayhook(self):
72 """Finish up all displayhook activities."""
72 """Finish up all displayhook activities."""
73 self.pub_socket.send_json(self.msg)
73 self.pub_socket.send_json(self.msg)
74 self.msg = None
74 self.msg = None
75
75
76
76
77 class ZMQInteractiveShell(InteractiveShell):
77 class ZMQInteractiveShell(InteractiveShell):
78 """A subclass of InteractiveShell for ZMQ."""
78 """A subclass of InteractiveShell for ZMQ."""
79
79
80 displayhook_class = Type(ZMQDisplayHook)
80 displayhook_class = Type(ZMQDisplayHook)
81
81
82 def init_environment(self):
82 def init_environment(self):
83 """Configure the user's environment.
83 """Configure the user's environment.
84
84
85 """
85 """
86 env = os.environ
86 env = os.environ
87 # These two ensure 'ls' produces nice coloring on BSD-derived systems
87 # These two ensure 'ls' produces nice coloring on BSD-derived systems
88 env['TERM'] = 'xterm-color'
88 env['TERM'] = 'xterm-color'
89 env['CLICOLOR'] = '1'
89 env['CLICOLOR'] = '1'
90 # Since normal pagers don't work at all (over pexpect we don't have
90 # Since normal pagers don't work at all (over pexpect we don't have
91 # single-key control of the subprocess), try to disable paging in
91 # single-key control of the subprocess), try to disable paging in
92 # subprocesses as much as possible.
92 # subprocesses as much as possible.
93 env['PAGER'] = 'cat'
93 env['PAGER'] = 'cat'
94 env['GIT_PAGER'] = 'cat'
94 env['GIT_PAGER'] = 'cat'
95
95
96 def auto_rewrite_input(self, cmd):
96 def auto_rewrite_input(self, cmd):
97 """Called to show the auto-rewritten input for autocall and friends.
97 """Called to show the auto-rewritten input for autocall and friends.
98
98
99 FIXME: this payload is currently not correctly processed by the
99 FIXME: this payload is currently not correctly processed by the
100 frontend.
100 frontend.
101 """
101 """
102 new = self.displayhook.prompt1.auto_rewrite() + cmd
102 new = self.displayhook.prompt1.auto_rewrite() + cmd
103 payload = dict(
103 payload = dict(
104 source='IPython.zmq.zmqshell.ZMQInteractiveShell.auto_rewrite_input',
104 source='IPython.zmq.zmqshell.ZMQInteractiveShell.auto_rewrite_input',
105 transformed_input=new,
105 transformed_input=new,
106 )
106 )
107 self.payload_manager.write_payload(payload)
107 self.payload_manager.write_payload(payload)
108
108
109 def ask_exit(self):
109 def ask_exit(self):
110 """Engage the exit actions."""
110 """Engage the exit actions."""
111 payload = dict(
111 payload = dict(
112 source='IPython.zmq.zmqshell.ZMQInteractiveShell.ask_exit',
112 source='IPython.zmq.zmqshell.ZMQInteractiveShell.ask_exit',
113 exit=True,
113 exit=True,
114 )
114 )
115 self.payload_manager.write_payload(payload)
115 self.payload_manager.write_payload(payload)
116
116
117 def _showtraceback(self, etype, evalue, stb):
117 def _showtraceback(self, etype, evalue, stb):
118
118
119 exc_content = {
119 exc_content = {
120 u'traceback' : stb,
120 u'traceback' : stb,
121 u'ename' : unicode(etype.__name__),
121 u'ename' : unicode(etype.__name__),
122 u'evalue' : unicode(evalue)
122 u'evalue' : unicode(evalue)
123 }
123 }
124
124
125 dh = self.displayhook
125 dh = self.displayhook
126 exc_msg = dh.session.msg(u'pyerr', exc_content, dh.parent_header)
126 exc_msg = dh.session.msg(u'pyerr', exc_content, dh.parent_header)
127 # Send exception info over pub socket for other clients than the caller
127 # Send exception info over pub socket for other clients than the caller
128 # to pick up
128 # to pick up
129 dh.pub_socket.send_json(exc_msg)
129 dh.pub_socket.send_json(exc_msg)
130
130
131 # FIXME - Hack: store exception info in shell object. Right now, the
131 # FIXME - Hack: store exception info in shell object. Right now, the
132 # caller is reading this info after the fact, we need to fix this logic
132 # caller is reading this info after the fact, we need to fix this logic
133 # to remove this hack. Even uglier, we need to store the error status
133 # to remove this hack. Even uglier, we need to store the error status
134 # here, because in the main loop, the logic that sets it is being
134 # here, because in the main loop, the logic that sets it is being
135 # skipped because runlines swallows the exceptions.
135 # skipped because runlines swallows the exceptions.
136 exc_content[u'status'] = u'error'
136 exc_content[u'status'] = u'error'
137 self._reply_content = exc_content
137 self._reply_content = exc_content
138 # /FIXME
138 # /FIXME
139
139
140 return exc_content
140 return exc_content
141
141
142 #------------------------------------------------------------------------
142 #------------------------------------------------------------------------
143 # Magic overrides
143 # Magic overrides
144 #------------------------------------------------------------------------
144 #------------------------------------------------------------------------
145 # Once the base class stops inheriting from magic, this code needs to be
145 # Once the base class stops inheriting from magic, this code needs to be
146 # moved into a separate machinery as well. For now, at least isolate here
146 # moved into a separate machinery as well. For now, at least isolate here
147 # the magics which this class needs to implement differently from the base
147 # the magics which this class needs to implement differently from the base
148 # class, or that are unique to it.
148 # class, or that are unique to it.
149
149
150 def magic_doctest_mode(self,parameter_s=''):
150 def magic_doctest_mode(self,parameter_s=''):
151 """Toggle doctest mode on and off.
151 """Toggle doctest mode on and off.
152
152
153 This mode is intended to make IPython behave as much as possible like a
153 This mode is intended to make IPython behave as much as possible like a
154 plain Python shell, from the perspective of how its prompts, exceptions
154 plain Python shell, from the perspective of how its prompts, exceptions
155 and output look. This makes it easy to copy and paste parts of a
155 and output look. This makes it easy to copy and paste parts of a
156 session into doctests. It does so by:
156 session into doctests. It does so by:
157
157
158 - Changing the prompts to the classic ``>>>`` ones.
158 - Changing the prompts to the classic ``>>>`` ones.
159 - Changing the exception reporting mode to 'Plain'.
159 - Changing the exception reporting mode to 'Plain'.
160 - Disabling pretty-printing of output.
160 - Disabling pretty-printing of output.
161
161
162 Note that IPython also supports the pasting of code snippets that have
162 Note that IPython also supports the pasting of code snippets that have
163 leading '>>>' and '...' prompts in them. This means that you can paste
163 leading '>>>' and '...' prompts in them. This means that you can paste
164 doctests from files or docstrings (even if they have leading
164 doctests from files or docstrings (even if they have leading
165 whitespace), and the code will execute correctly. You can then use
165 whitespace), and the code will execute correctly. You can then use
166 '%history -t' to see the translated history; this will give you the
166 '%history -t' to see the translated history; this will give you the
167 input after removal of all the leading prompts and whitespace, which
167 input after removal of all the leading prompts and whitespace, which
168 can be pasted back into an editor.
168 can be pasted back into an editor.
169
169
170 With these features, you can switch into this mode easily whenever you
170 With these features, you can switch into this mode easily whenever you
171 need to do testing and changes to doctests, without having to leave
171 need to do testing and changes to doctests, without having to leave
172 your existing IPython session.
172 your existing IPython session.
173 """
173 """
174
174
175 from IPython.utils.ipstruct import Struct
175 from IPython.utils.ipstruct import Struct
176
176
177 # Shorthands
177 # Shorthands
178 shell = self.shell
178 shell = self.shell
179 # dstore is a data store kept in the instance metadata bag to track any
179 # dstore is a data store kept in the instance metadata bag to track any
180 # changes we make, so we can undo them later.
180 # changes we make, so we can undo them later.
181 dstore = shell.meta.setdefault('doctest_mode', Struct())
181 dstore = shell.meta.setdefault('doctest_mode', Struct())
182 save_dstore = dstore.setdefault
182 save_dstore = dstore.setdefault
183
183
184 # save a few values we'll need to recover later
184 # save a few values we'll need to recover later
185 mode = save_dstore('mode', False)
185 mode = save_dstore('mode', False)
186 save_dstore('rc_pprint', shell.pprint)
186 save_dstore('rc_pprint', shell.pprint)
187 save_dstore('xmode', shell.InteractiveTB.mode)
187 save_dstore('xmode', shell.InteractiveTB.mode)
188
188
189 if mode == False:
189 if mode == False:
190 # turn on
190 # turn on
191 shell.pprint = False
191 shell.pprint = False
192 shell.magic_xmode('Plain')
192 shell.magic_xmode('Plain')
193 else:
193 else:
194 # turn off
194 # turn off
195 shell.pprint = dstore.rc_pprint
195 shell.pprint = dstore.rc_pprint
196 shell.magic_xmode(dstore.xmode)
196 shell.magic_xmode(dstore.xmode)
197
197
198 # Store new mode and inform on console
198 # Store new mode and inform on console
199 dstore.mode = bool(1-int(mode))
199 dstore.mode = bool(1-int(mode))
200 mode_label = ['OFF','ON'][dstore.mode]
200 mode_label = ['OFF','ON'][dstore.mode]
201 print('Doctest mode is:', mode_label)
201 print('Doctest mode is:', mode_label)
202
202
203 # Send the payload back so that clients can modify their prompt display
203 # Send the payload back so that clients can modify their prompt display
204 payload = dict(
204 payload = dict(
205 source='IPython.zmq.zmqshell.ZMQInteractiveShell.magic_doctest_mode',
205 source='IPython.zmq.zmqshell.ZMQInteractiveShell.magic_doctest_mode',
206 mode=dstore.mode)
206 mode=dstore.mode)
207 self.payload_manager.write_payload(payload)
207 self.payload_manager.write_payload(payload)
208
208
209 def magic_edit(self,parameter_s='',last_call=['','']):
209 def magic_edit(self,parameter_s='',last_call=['','']):
210 """Bring up an editor and execute the resulting code.
210 """Bring up an editor and execute the resulting code.
211
211
212 Usage:
212 Usage:
213 %edit [options] [args]
213 %edit [options] [args]
214
214
215 %edit runs IPython's editor hook. The default version of this hook is
215 %edit runs IPython's editor hook. The default version of this hook is
216 set to call the __IPYTHON__.rc.editor command. This is read from your
216 set to call the __IPYTHON__.rc.editor command. This is read from your
217 environment variable $EDITOR. If this isn't found, it will default to
217 environment variable $EDITOR. If this isn't found, it will default to
218 vi under Linux/Unix and to notepad under Windows. See the end of this
218 vi under Linux/Unix and to notepad under Windows. See the end of this
219 docstring for how to change the editor hook.
219 docstring for how to change the editor hook.
220
220
221 You can also set the value of this editor via the command line option
221 You can also set the value of this editor via the command line option
222 '-editor' or in your ipythonrc file. This is useful if you wish to use
222 '-editor' or in your ipythonrc file. This is useful if you wish to use
223 specifically for IPython an editor different from your typical default
223 specifically for IPython an editor different from your typical default
224 (and for Windows users who typically don't set environment variables).
224 (and for Windows users who typically don't set environment variables).
225
225
226 This command allows you to conveniently edit multi-line code right in
226 This command allows you to conveniently edit multi-line code right in
227 your IPython session.
227 your IPython session.
228
228
229 If called without arguments, %edit opens up an empty editor with a
229 If called without arguments, %edit opens up an empty editor with a
230 temporary file and will execute the contents of this file when you
230 temporary file and will execute the contents of this file when you
231 close it (don't forget to save it!).
231 close it (don't forget to save it!).
232
232
233
233
234 Options:
234 Options:
235
235
236 -n <number>: open the editor at a specified line number. By default,
236 -n <number>: open the editor at a specified line number. By default,
237 the IPython editor hook uses the unix syntax 'editor +N filename', but
237 the IPython editor hook uses the unix syntax 'editor +N filename', but
238 you can configure this by providing your own modified hook if your
238 you can configure this by providing your own modified hook if your
239 favorite editor supports line-number specifications with a different
239 favorite editor supports line-number specifications with a different
240 syntax.
240 syntax.
241
241
242 -p: this will call the editor with the same data as the previous time
242 -p: this will call the editor with the same data as the previous time
243 it was used, regardless of how long ago (in your current session) it
243 it was used, regardless of how long ago (in your current session) it
244 was.
244 was.
245
245
246 -r: use 'raw' input. This option only applies to input taken from the
246 -r: use 'raw' input. This option only applies to input taken from the
247 user's history. By default, the 'processed' history is used, so that
247 user's history. By default, the 'processed' history is used, so that
248 magics are loaded in their transformed version to valid Python. If
248 magics are loaded in their transformed version to valid Python. If
249 this option is given, the raw input as typed as the command line is
249 this option is given, the raw input as typed as the command line is
250 used instead. When you exit the editor, it will be executed by
250 used instead. When you exit the editor, it will be executed by
251 IPython's own processor.
251 IPython's own processor.
252
252
253 -x: do not execute the edited code immediately upon exit. This is
253 -x: do not execute the edited code immediately upon exit. This is
254 mainly useful if you are editing programs which need to be called with
254 mainly useful if you are editing programs which need to be called with
255 command line arguments, which you can then do using %run.
255 command line arguments, which you can then do using %run.
256
256
257
257
258 Arguments:
258 Arguments:
259
259
260 If arguments are given, the following possibilites exist:
260 If arguments are given, the following possibilites exist:
261
261
262 - The arguments are numbers or pairs of colon-separated numbers (like
262 - The arguments are numbers or pairs of colon-separated numbers (like
263 1 4:8 9). These are interpreted as lines of previous input to be
263 1 4:8 9). These are interpreted as lines of previous input to be
264 loaded into the editor. The syntax is the same of the %macro command.
264 loaded into the editor. The syntax is the same of the %macro command.
265
265
266 - If the argument doesn't start with a number, it is evaluated as a
266 - If the argument doesn't start with a number, it is evaluated as a
267 variable and its contents loaded into the editor. You can thus edit
267 variable and its contents loaded into the editor. You can thus edit
268 any string which contains python code (including the result of
268 any string which contains python code (including the result of
269 previous edits).
269 previous edits).
270
270
271 - If the argument is the name of an object (other than a string),
271 - If the argument is the name of an object (other than a string),
272 IPython will try to locate the file where it was defined and open the
272 IPython will try to locate the file where it was defined and open the
273 editor at the point where it is defined. You can use `%edit function`
273 editor at the point where it is defined. You can use `%edit function`
274 to load an editor exactly at the point where 'function' is defined,
274 to load an editor exactly at the point where 'function' is defined,
275 edit it and have the file be executed automatically.
275 edit it and have the file be executed automatically.
276
276
277 If the object is a macro (see %macro for details), this opens up your
277 If the object is a macro (see %macro for details), this opens up your
278 specified editor with a temporary file containing the macro's data.
278 specified editor with a temporary file containing the macro's data.
279 Upon exit, the macro is reloaded with the contents of the file.
279 Upon exit, the macro is reloaded with the contents of the file.
280
280
281 Note: opening at an exact line is only supported under Unix, and some
281 Note: opening at an exact line is only supported under Unix, and some
282 editors (like kedit and gedit up to Gnome 2.8) do not understand the
282 editors (like kedit and gedit up to Gnome 2.8) do not understand the
283 '+NUMBER' parameter necessary for this feature. Good editors like
283 '+NUMBER' parameter necessary for this feature. Good editors like
284 (X)Emacs, vi, jed, pico and joe all do.
284 (X)Emacs, vi, jed, pico and joe all do.
285
285
286 - If the argument is not found as a variable, IPython will look for a
286 - If the argument is not found as a variable, IPython will look for a
287 file with that name (adding .py if necessary) and load it into the
287 file with that name (adding .py if necessary) and load it into the
288 editor. It will execute its contents with execfile() when you exit,
288 editor. It will execute its contents with execfile() when you exit,
289 loading any code in the file into your interactive namespace.
289 loading any code in the file into your interactive namespace.
290
290
291 After executing your code, %edit will return as output the code you
291 After executing your code, %edit will return as output the code you
292 typed in the editor (except when it was an existing file). This way
292 typed in the editor (except when it was an existing file). This way
293 you can reload the code in further invocations of %edit as a variable,
293 you can reload the code in further invocations of %edit as a variable,
294 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
294 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
295 the output.
295 the output.
296
296
297 Note that %edit is also available through the alias %ed.
297 Note that %edit is also available through the alias %ed.
298
298
299 This is an example of creating a simple function inside the editor and
299 This is an example of creating a simple function inside the editor and
300 then modifying it. First, start up the editor:
300 then modifying it. First, start up the editor:
301
301
302 In [1]: ed
302 In [1]: ed
303 Editing... done. Executing edited code...
303 Editing... done. Executing edited code...
304 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
304 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
305
305
306 We can then call the function foo():
306 We can then call the function foo():
307
307
308 In [2]: foo()
308 In [2]: foo()
309 foo() was defined in an editing session
309 foo() was defined in an editing session
310
310
311 Now we edit foo. IPython automatically loads the editor with the
311 Now we edit foo. IPython automatically loads the editor with the
312 (temporary) file where foo() was previously defined:
312 (temporary) file where foo() was previously defined:
313
313
314 In [3]: ed foo
314 In [3]: ed foo
315 Editing... done. Executing edited code...
315 Editing... done. Executing edited code...
316
316
317 And if we call foo() again we get the modified version:
317 And if we call foo() again we get the modified version:
318
318
319 In [4]: foo()
319 In [4]: foo()
320 foo() has now been changed!
320 foo() has now been changed!
321
321
322 Here is an example of how to edit a code snippet successive
322 Here is an example of how to edit a code snippet successive
323 times. First we call the editor:
323 times. First we call the editor:
324
324
325 In [5]: ed
325 In [5]: ed
326 Editing... done. Executing edited code...
326 Editing... done. Executing edited code...
327 hello
327 hello
328 Out[5]: "print 'hello'n"
328 Out[5]: "print 'hello'n"
329
329
330 Now we call it again with the previous output (stored in _):
330 Now we call it again with the previous output (stored in _):
331
331
332 In [6]: ed _
332 In [6]: ed _
333 Editing... done. Executing edited code...
333 Editing... done. Executing edited code...
334 hello world
334 hello world
335 Out[6]: "print 'hello world'n"
335 Out[6]: "print 'hello world'n"
336
336
337 Now we call it with the output #8 (stored in _8, also as Out[8]):
337 Now we call it with the output #8 (stored in _8, also as Out[8]):
338
338
339 In [7]: ed _8
339 In [7]: ed _8
340 Editing... done. Executing edited code...
340 Editing... done. Executing edited code...
341 hello again
341 hello again
342 Out[7]: "print 'hello again'n"
342 Out[7]: "print 'hello again'n"
343
343
344
344
345 Changing the default editor hook:
345 Changing the default editor hook:
346
346
347 If you wish to write your own editor hook, you can put it in a
347 If you wish to write your own editor hook, you can put it in a
348 configuration file which you load at startup time. The default hook
348 configuration file which you load at startup time. The default hook
349 is defined in the IPython.core.hooks module, and you can use that as a
349 is defined in the IPython.core.hooks module, and you can use that as a
350 starting example for further modifications. That file also has
350 starting example for further modifications. That file also has
351 general instructions on how to set a new hook for use once you've
351 general instructions on how to set a new hook for use once you've
352 defined it."""
352 defined it."""
353
353
354 # FIXME: This function has become a convoluted mess. It needs a
354 # FIXME: This function has become a convoluted mess. It needs a
355 # ground-up rewrite with clean, simple logic.
355 # ground-up rewrite with clean, simple logic.
356
356
357 def make_filename(arg):
357 def make_filename(arg):
358 "Make a filename from the given args"
358 "Make a filename from the given args"
359 try:
359 try:
360 filename = get_py_filename(arg)
360 filename = get_py_filename(arg)
361 except IOError:
361 except IOError:
362 if args.endswith('.py'):
362 if args.endswith('.py'):
363 filename = arg
363 filename = arg
364 else:
364 else:
365 filename = None
365 filename = None
366 return filename
366 return filename
367
367
368 # custom exceptions
368 # custom exceptions
369 class DataIsObject(Exception): pass
369 class DataIsObject(Exception): pass
370
370
371 opts,args = self.parse_options(parameter_s,'prn:')
371 opts,args = self.parse_options(parameter_s,'prn:')
372 # Set a few locals from the options for convenience:
372 # Set a few locals from the options for convenience:
373 opts_p = opts.has_key('p')
373 opts_p = opts.has_key('p')
374 opts_r = opts.has_key('r')
374 opts_r = opts.has_key('r')
375
375
376 # Default line number value
376 # Default line number value
377 lineno = opts.get('n',None)
377 lineno = opts.get('n',None)
378 if lineno is not None:
378 if lineno is not None:
379 try:
379 try:
380 lineno = int(lineno)
380 lineno = int(lineno)
381 except:
381 except:
382 warn("The -n argument must be an integer.")
382 warn("The -n argument must be an integer.")
383 return
383 return
384
384
385 if opts_p:
385 if opts_p:
386 args = '_%s' % last_call[0]
386 args = '_%s' % last_call[0]
387 if not self.shell.user_ns.has_key(args):
387 if not self.shell.user_ns.has_key(args):
388 args = last_call[1]
388 args = last_call[1]
389
389
390 # use last_call to remember the state of the previous call, but don't
390 # use last_call to remember the state of the previous call, but don't
391 # let it be clobbered by successive '-p' calls.
391 # let it be clobbered by successive '-p' calls.
392 try:
392 try:
393 last_call[0] = self.shell.displayhook.prompt_count
393 last_call[0] = self.shell.displayhook.prompt_count
394 if not opts_p:
394 if not opts_p:
395 last_call[1] = parameter_s
395 last_call[1] = parameter_s
396 except:
396 except:
397 pass
397 pass
398
398
399 # by default this is done with temp files, except when the given
399 # by default this is done with temp files, except when the given
400 # arg is a filename
400 # arg is a filename
401 use_temp = 1
401 use_temp = 1
402
402
403 if re.match(r'\d',args):
403 if re.match(r'\d',args):
404 # Mode where user specifies ranges of lines, like in %macro.
404 # Mode where user specifies ranges of lines, like in %macro.
405 # This means that you can't edit files whose names begin with
405 # This means that you can't edit files whose names begin with
406 # numbers this way. Tough.
406 # numbers this way. Tough.
407 ranges = args.split()
407 ranges = args.split()
408 data = ''.join(self.extract_input_slices(ranges,opts_r))
408 data = ''.join(self.extract_input_slices(ranges,opts_r))
409 elif args.endswith('.py'):
409 elif args.endswith('.py'):
410 filename = make_filename(args)
410 filename = make_filename(args)
411 data = ''
411 data = ''
412 use_temp = 0
412 use_temp = 0
413 elif args:
413 elif args:
414 try:
414 try:
415 # Load the parameter given as a variable. If not a string,
415 # Load the parameter given as a variable. If not a string,
416 # process it as an object instead (below)
416 # process it as an object instead (below)
417
417
418 #print '*** args',args,'type',type(args) # dbg
418 #print '*** args',args,'type',type(args) # dbg
419 data = eval(args,self.shell.user_ns)
419 data = eval(args,self.shell.user_ns)
420 if not type(data) in StringTypes:
420 if not type(data) in StringTypes:
421 raise DataIsObject
421 raise DataIsObject
422
422
423 except (NameError,SyntaxError):
423 except (NameError,SyntaxError):
424 # given argument is not a variable, try as a filename
424 # given argument is not a variable, try as a filename
425 filename = make_filename(args)
425 filename = make_filename(args)
426 if filename is None:
426 if filename is None:
427 warn("Argument given (%s) can't be found as a variable "
427 warn("Argument given (%s) can't be found as a variable "
428 "or as a filename." % args)
428 "or as a filename." % args)
429 return
429 return
430
430
431 data = ''
431 data = ''
432 use_temp = 0
432 use_temp = 0
433 except DataIsObject:
433 except DataIsObject:
434
434
435 # macros have a special edit function
435 # macros have a special edit function
436 if isinstance(data,Macro):
436 if isinstance(data,Macro):
437 self._edit_macro(args,data)
437 self._edit_macro(args,data)
438 return
438 return
439
439
440 # For objects, try to edit the file where they are defined
440 # For objects, try to edit the file where they are defined
441 try:
441 try:
442 filename = inspect.getabsfile(data)
442 filename = inspect.getabsfile(data)
443 if 'fakemodule' in filename.lower() and inspect.isclass(data):
443 if 'fakemodule' in filename.lower() and inspect.isclass(data):
444 # class created by %edit? Try to find source
444 # class created by %edit? Try to find source
445 # by looking for method definitions instead, the
445 # by looking for method definitions instead, the
446 # __module__ in those classes is FakeModule.
446 # __module__ in those classes is FakeModule.
447 attrs = [getattr(data, aname) for aname in dir(data)]
447 attrs = [getattr(data, aname) for aname in dir(data)]
448 for attr in attrs:
448 for attr in attrs:
449 if not inspect.ismethod(attr):
449 if not inspect.ismethod(attr):
450 continue
450 continue
451 filename = inspect.getabsfile(attr)
451 filename = inspect.getabsfile(attr)
452 if filename and 'fakemodule' not in filename.lower():
452 if filename and 'fakemodule' not in filename.lower():
453 # change the attribute to be the edit target instead
453 # change the attribute to be the edit target instead
454 data = attr
454 data = attr
455 break
455 break
456
456
457 datafile = 1
457 datafile = 1
458 except TypeError:
458 except TypeError:
459 filename = make_filename(args)
459 filename = make_filename(args)
460 datafile = 1
460 datafile = 1
461 warn('Could not find file where `%s` is defined.\n'
461 warn('Could not find file where `%s` is defined.\n'
462 'Opening a file named `%s`' % (args,filename))
462 'Opening a file named `%s`' % (args,filename))
463 # Now, make sure we can actually read the source (if it was in
463 # Now, make sure we can actually read the source (if it was in
464 # a temp file it's gone by now).
464 # a temp file it's gone by now).
465 if datafile:
465 if datafile:
466 try:
466 try:
467 if lineno is None:
467 if lineno is None:
468 lineno = inspect.getsourcelines(data)[1]
468 lineno = inspect.getsourcelines(data)[1]
469 except IOError:
469 except IOError:
470 filename = make_filename(args)
470 filename = make_filename(args)
471 if filename is None:
471 if filename is None:
472 warn('The file `%s` where `%s` was defined cannot '
472 warn('The file `%s` where `%s` was defined cannot '
473 'be read.' % (filename,data))
473 'be read.' % (filename,data))
474 return
474 return
475 use_temp = 0
475 use_temp = 0
476 else:
476 else:
477 data = ''
477 data = ''
478
478
479 if use_temp:
479 if use_temp:
480 filename = self.shell.mktempfile(data)
480 filename = self.shell.mktempfile(data)
481 print('IPython will make a temporary file named:', filename)
481 print('IPython will make a temporary file named:', filename)
482
482
483 # Make sure we send to the client an absolute path, in case the working
483 # Make sure we send to the client an absolute path, in case the working
484 # directory of client and kernel don't match
484 # directory of client and kernel don't match
485 filename = os.path.abspath(filename)
485 filename = os.path.abspath(filename)
486
486
487 payload = {
487 payload = {
488 'source' : 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic',
488 'source' : 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic',
489 'filename' : filename,
489 'filename' : filename,
490 'line_number' : lineno
490 'line_number' : lineno
491 }
491 }
492 self.payload_manager.write_payload(payload)
492 self.payload_manager.write_payload(payload)
493
493
494 def magic_gui(self, *args, **kwargs):
494 def magic_gui(self, *args, **kwargs):
495 raise NotImplementedError(
495 raise NotImplementedError(
496 'GUI support must be enabled in command line options.')
496 'GUI support must be enabled in command line options.')
497
497
498 def magic_pylab(self, *args, **kwargs):
498 def magic_pylab(self, *args, **kwargs):
499 raise NotImplementedError(
499 raise NotImplementedError(
500 'pylab support must be enabled in command line options.')
500 'pylab support must be enabled in command line options.')
501
501
502 # A few magics that are adapted to the specifics of using pexpect and a
502 # A few magics that are adapted to the specifics of using pexpect and a
503 # remote terminal
503 # remote terminal
504
504
505 def magic_clear(self, arg_s):
505 def magic_clear(self, arg_s):
506 """Clear the terminal."""
506 """Clear the terminal."""
507 if os.name == 'posix':
507 if os.name == 'posix':
508 self.shell.system("clear")
508 self.shell.system("clear")
509 else:
509 else:
510 self.shell.system("cls")
510 self.shell.system("cls")
511
511
512 if os.name == 'nt':
512 if os.name == 'nt':
513 # This is the usual name in windows
513 # This is the usual name in windows
514 magic_cls = magic_clear
514 magic_cls = magic_clear
515
515
516 # Terminal pagers won't work over pexpect, but we do have our own pager
516 # Terminal pagers won't work over pexpect, but we do have our own pager
517
517
518 def magic_less(self, arg_s):
518 def magic_less(self, arg_s):
519 """Show a file through the pager.
519 """Show a file through the pager.
520
520
521 Files ending in .py are syntax-highlighted."""
521 Files ending in .py are syntax-highlighted."""
522 cont = open(arg_s).read()
522 cont = open(arg_s).read()
523 if arg_s.endswith('.py'):
523 if arg_s.endswith('.py'):
524 cont = self.shell.pycolorize(cont)
524 cont = self.shell.pycolorize(cont)
525 page.page(cont)
525 page.page(cont)
526
526
527 magic_more = magic_less
527 magic_more = magic_less
528
528
529 # Man calls a pager, so we also need to redefine it
529 # Man calls a pager, so we also need to redefine it
530 if os.name == 'posix':
530 if os.name == 'posix':
531 def magic_man(self, arg_s):
531 def magic_man(self, arg_s):
532 """Find the man page for the given command and display in pager."""
532 """Find the man page for the given command and display in pager."""
533 page.page(self.shell.getoutput('man %s | col -b' % arg_s,
533 page.page(self.shell.getoutput('man %s | col -b' % arg_s,
534 split=False))
534 split=False))
535
535
536 # FIXME: this is specific to the GUI, so we should let the gui app load
536 # FIXME: this is specific to the GUI, so we should let the gui app load
537 # magics at startup that are only for the gui. Once the gui app has proper
537 # magics at startup that are only for the gui. Once the gui app has proper
538 # profile and configuration management, we can have it initialize a kernel
538 # profile and configuration management, we can have it initialize a kernel
539 # with a special config file that provides these.
539 # with a special config file that provides these.
540 def magic_guiref(self, arg_s):
540 def magic_guiref(self, arg_s):
541 """Show a basic reference about the GUI console."""
541 """Show a basic reference about the GUI console."""
542 from IPython.core.usage import gui_reference
542 from IPython.core.usage import gui_reference
543 page.page(gui_reference, auto_html=True)
543 page.page(gui_reference, auto_html=True)
544
544
545 def magic_loadpy(self, arg_s):
546 """Load a .py python script into the GUI console.
547
548 This magic command can either take a local filename or a url::
549
550 %loadpy myscript.py
551 %loadpy http://www.example.com/myscript.py
552 """
553 if not arg_s.endswith('.py'):
554 raise ValueError('%%load only works with .py files: %s' % arg_s)
555 if arg_s.startswith('http'):
556 import urllib2
557 response = urllib2.urlopen(arg_s)
558 content = response.read()
559 else:
560 content = open(arg_s).read()
561 payload = dict(
562 source='IPython.zmq.zmqshell.ZMQInteractiveShell.magic_loadpy',
563 text=content
564 )
565 self.payload_manager.write_payload(payload)
545
566
546 InteractiveShellABC.register(ZMQInteractiveShell)
567 InteractiveShellABC.register(ZMQInteractiveShell)
General Comments 0
You need to be logged in to leave comments. Login now