##// END OF EJS Templates
Fixed line_number being inserted as a string into the %edit payload.
epatters -
Show More
@@ -1,293 +1,293 b''
1 # Standard library imports
1 # Standard library imports
2 from subprocess import Popen
2 from subprocess import Popen
3
3
4 # System library imports
4 # System library imports
5 from PyQt4 import QtCore, QtGui
5 from PyQt4 import QtCore, QtGui
6
6
7 # Local imports
7 # Local imports
8 from IPython.core.inputsplitter import IPythonInputSplitter
8 from IPython.core.inputsplitter import IPythonInputSplitter
9 from IPython.core.usage import default_banner
9 from IPython.core.usage import default_banner
10 from frontend_widget import FrontendWidget
10 from frontend_widget import FrontendWidget
11
11
12
12
13 class IPythonPromptBlock(object):
13 class IPythonPromptBlock(object):
14 """ An internal storage object for IPythonWidget.
14 """ An internal storage object for IPythonWidget.
15 """
15 """
16 def __init__(self, block, length, number):
16 def __init__(self, block, length, number):
17 self.block = block
17 self.block = block
18 self.length = length
18 self.length = length
19 self.number = number
19 self.number = number
20
20
21
21
22 class IPythonWidget(FrontendWidget):
22 class IPythonWidget(FrontendWidget):
23 """ A FrontendWidget for an IPython kernel.
23 """ A FrontendWidget for an IPython kernel.
24 """
24 """
25
25
26 # Signal emitted when an editor is needed for a file and the editor has been
26 # Signal emitted when an editor is needed for a file and the editor has been
27 # specified as 'custom'. See 'set_editor' for more information.
27 # specified as 'custom'. See 'set_editor' for more information.
28 custom_edit_requested = QtCore.pyqtSignal(object, int)
28 custom_edit_requested = QtCore.pyqtSignal(object, object)
29
29
30 # The default stylesheet: black text on a white background.
30 # The default stylesheet: black text on a white background.
31 default_stylesheet = """
31 default_stylesheet = """
32 .error { color: red; }
32 .error { color: red; }
33 .in-prompt { color: navy; }
33 .in-prompt { color: navy; }
34 .in-prompt-number { font-weight: bold; }
34 .in-prompt-number { font-weight: bold; }
35 .out-prompt { color: darkred; }
35 .out-prompt { color: darkred; }
36 .out-prompt-number { font-weight: bold; }
36 .out-prompt-number { font-weight: bold; }
37 """
37 """
38
38
39 # A dark stylesheet: white text on a black background.
39 # A dark stylesheet: white text on a black background.
40 dark_stylesheet = """
40 dark_stylesheet = """
41 QPlainTextEdit, QTextEdit { background-color: black; color: white }
41 QPlainTextEdit, QTextEdit { background-color: black; color: white }
42 QFrame { border: 1px solid grey; }
42 QFrame { border: 1px solid grey; }
43 .error { color: red; }
43 .error { color: red; }
44 .in-prompt { color: lime; }
44 .in-prompt { color: lime; }
45 .in-prompt-number { color: lime; font-weight: bold; }
45 .in-prompt-number { color: lime; font-weight: bold; }
46 .out-prompt { color: red; }
46 .out-prompt { color: red; }
47 .out-prompt-number { color: red; font-weight: bold; }
47 .out-prompt-number { color: red; font-weight: bold; }
48 """
48 """
49
49
50 # Default prompts.
50 # Default prompts.
51 in_prompt = 'In [<span class="in-prompt-number">%i</span>]: '
51 in_prompt = 'In [<span class="in-prompt-number">%i</span>]: '
52 out_prompt = 'Out[<span class="out-prompt-number">%i</span>]: '
52 out_prompt = 'Out[<span class="out-prompt-number">%i</span>]: '
53
53
54 # FrontendWidget protected class variables.
54 # FrontendWidget protected class variables.
55 #_input_splitter_class = IPythonInputSplitter
55 #_input_splitter_class = IPythonInputSplitter
56
56
57 # IPythonWidget protected class variables.
57 # IPythonWidget protected class variables.
58 _payload_source_edit = 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic'
58 _payload_source_edit = 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic'
59 _payload_source_page = 'IPython.zmq.page.page'
59 _payload_source_page = 'IPython.zmq.page.page'
60
60
61 #---------------------------------------------------------------------------
61 #---------------------------------------------------------------------------
62 # 'object' interface
62 # 'object' interface
63 #---------------------------------------------------------------------------
63 #---------------------------------------------------------------------------
64
64
65 def __init__(self, *args, **kw):
65 def __init__(self, *args, **kw):
66 super(IPythonWidget, self).__init__(*args, **kw)
66 super(IPythonWidget, self).__init__(*args, **kw)
67
67
68 # IPythonWidget protected variables.
68 # IPythonWidget protected variables.
69 self._previous_prompt_obj = None
69 self._previous_prompt_obj = None
70
70
71 # Set a default editor and stylesheet.
71 # Set a default editor and stylesheet.
72 self.set_editor('default')
72 self.set_editor('default')
73 self.reset_styling()
73 self.reset_styling()
74
74
75 #---------------------------------------------------------------------------
75 #---------------------------------------------------------------------------
76 # 'BaseFrontendMixin' abstract interface
76 # 'BaseFrontendMixin' abstract interface
77 #---------------------------------------------------------------------------
77 #---------------------------------------------------------------------------
78
78
79 def _handle_pyout(self, msg):
79 def _handle_pyout(self, msg):
80 """ Reimplemented for IPython-style "display hook".
80 """ Reimplemented for IPython-style "display hook".
81 """
81 """
82 if not self._hidden and self._is_from_this_session(msg):
82 if not self._hidden and self._is_from_this_session(msg):
83 content = msg['content']
83 content = msg['content']
84 prompt_number = content['prompt_number']
84 prompt_number = content['prompt_number']
85 self._append_plain_text(content['output_sep'])
85 self._append_plain_text(content['output_sep'])
86 self._append_html(self._make_out_prompt(prompt_number))
86 self._append_html(self._make_out_prompt(prompt_number))
87 self._append_plain_text(content['data'] + '\n' +
87 self._append_plain_text(content['data'] + '\n' +
88 content['output_sep2'])
88 content['output_sep2'])
89
89
90 #---------------------------------------------------------------------------
90 #---------------------------------------------------------------------------
91 # 'FrontendWidget' interface
91 # 'FrontendWidget' interface
92 #---------------------------------------------------------------------------
92 #---------------------------------------------------------------------------
93
93
94 def execute_file(self, path, hidden=False):
94 def execute_file(self, path, hidden=False):
95 """ Reimplemented to use the 'run' magic.
95 """ Reimplemented to use the 'run' magic.
96 """
96 """
97 self.execute('run %s' % path, hidden=hidden)
97 self.execute('run %s' % path, hidden=hidden)
98
98
99 #---------------------------------------------------------------------------
99 #---------------------------------------------------------------------------
100 # 'FrontendWidget' protected interface
100 # 'FrontendWidget' protected interface
101 #---------------------------------------------------------------------------
101 #---------------------------------------------------------------------------
102
102
103 def _get_banner(self):
103 def _get_banner(self):
104 """ Reimplemented to return IPython's default banner.
104 """ Reimplemented to return IPython's default banner.
105 """
105 """
106 return default_banner + '\n'
106 return default_banner + '\n'
107
107
108 def _process_execute_error(self, msg):
108 def _process_execute_error(self, msg):
109 """ Reimplemented for IPython-style traceback formatting.
109 """ Reimplemented for IPython-style traceback formatting.
110 """
110 """
111 content = msg['content']
111 content = msg['content']
112 traceback_lines = content['traceback'][:]
112 traceback_lines = content['traceback'][:]
113 traceback = ''.join(traceback_lines)
113 traceback = ''.join(traceback_lines)
114 traceback = traceback.replace(' ', '&nbsp;')
114 traceback = traceback.replace(' ', '&nbsp;')
115 traceback = traceback.replace('\n', '<br/>')
115 traceback = traceback.replace('\n', '<br/>')
116
116
117 ename = content['ename']
117 ename = content['ename']
118 ename_styled = '<span class="error">%s</span>' % ename
118 ename_styled = '<span class="error">%s</span>' % ename
119 traceback = traceback.replace(ename, ename_styled)
119 traceback = traceback.replace(ename, ename_styled)
120
120
121 self._append_html(traceback)
121 self._append_html(traceback)
122
122
123 def _process_execute_payload(self, item):
123 def _process_execute_payload(self, item):
124 """ Reimplemented to handle %edit and paging payloads.
124 """ Reimplemented to handle %edit and paging payloads.
125 """
125 """
126 if item['source'] == self._payload_source_edit:
126 if item['source'] == self._payload_source_edit:
127 self.edit(item['filename'], item['line_number'])
127 self.edit(item['filename'], item['line_number'])
128 return True
128 return True
129 elif item['source'] == self._payload_source_page:
129 elif item['source'] == self._payload_source_page:
130 self._page(item['data'])
130 self._page(item['data'])
131 return True
131 return True
132 else:
132 else:
133 return False
133 return False
134
134
135 def _show_interpreter_prompt(self, number=None, input_sep='\n'):
135 def _show_interpreter_prompt(self, number=None, input_sep='\n'):
136 """ Reimplemented for IPython-style prompts.
136 """ Reimplemented for IPython-style prompts.
137 """
137 """
138 # TODO: If a number was not specified, make a prompt number request.
138 # TODO: If a number was not specified, make a prompt number request.
139 if number is None:
139 if number is None:
140 number = 0
140 number = 0
141
141
142 # Show a new prompt and save information about it so that it can be
142 # Show a new prompt and save information about it so that it can be
143 # updated later if the prompt number turns out to be wrong.
143 # updated later if the prompt number turns out to be wrong.
144 self._append_plain_text(input_sep)
144 self._append_plain_text(input_sep)
145 self._show_prompt(self._make_in_prompt(number), html=True)
145 self._show_prompt(self._make_in_prompt(number), html=True)
146 block = self._control.document().lastBlock()
146 block = self._control.document().lastBlock()
147 length = len(self._prompt)
147 length = len(self._prompt)
148 self._previous_prompt_obj = IPythonPromptBlock(block, length, number)
148 self._previous_prompt_obj = IPythonPromptBlock(block, length, number)
149
149
150 # Update continuation prompt to reflect (possibly) new prompt length.
150 # Update continuation prompt to reflect (possibly) new prompt length.
151 self._set_continuation_prompt(
151 self._set_continuation_prompt(
152 self._make_continuation_prompt(self._prompt), html=True)
152 self._make_continuation_prompt(self._prompt), html=True)
153
153
154 def _show_interpreter_prompt_for_reply(self, msg):
154 def _show_interpreter_prompt_for_reply(self, msg):
155 """ Reimplemented for IPython-style prompts.
155 """ Reimplemented for IPython-style prompts.
156 """
156 """
157 # Update the old prompt number if necessary.
157 # Update the old prompt number if necessary.
158 content = msg['content']
158 content = msg['content']
159 previous_prompt_number = content['prompt_number']
159 previous_prompt_number = content['prompt_number']
160 if self._previous_prompt_obj and \
160 if self._previous_prompt_obj and \
161 self._previous_prompt_obj.number != previous_prompt_number:
161 self._previous_prompt_obj.number != previous_prompt_number:
162 block = self._previous_prompt_obj.block
162 block = self._previous_prompt_obj.block
163 if block.isValid():
163 if block.isValid():
164
164
165 # Remove the old prompt and insert a new prompt.
165 # Remove the old prompt and insert a new prompt.
166 cursor = QtGui.QTextCursor(block)
166 cursor = QtGui.QTextCursor(block)
167 cursor.movePosition(QtGui.QTextCursor.Right,
167 cursor.movePosition(QtGui.QTextCursor.Right,
168 QtGui.QTextCursor.KeepAnchor,
168 QtGui.QTextCursor.KeepAnchor,
169 self._previous_prompt_obj.length)
169 self._previous_prompt_obj.length)
170 prompt = self._make_in_prompt(previous_prompt_number)
170 prompt = self._make_in_prompt(previous_prompt_number)
171 self._prompt = self._insert_html_fetching_plain_text(
171 self._prompt = self._insert_html_fetching_plain_text(
172 cursor, prompt)
172 cursor, prompt)
173
173
174 # When the HTML is inserted, Qt blows away the syntax
174 # When the HTML is inserted, Qt blows away the syntax
175 # highlighting for the line, so we need to rehighlight it.
175 # highlighting for the line, so we need to rehighlight it.
176 self._highlighter.rehighlightBlock(cursor.block())
176 self._highlighter.rehighlightBlock(cursor.block())
177
177
178 self._previous_prompt_obj = None
178 self._previous_prompt_obj = None
179
179
180 # Show a new prompt with the kernel's estimated prompt number.
180 # Show a new prompt with the kernel's estimated prompt number.
181 next_prompt = content['next_prompt']
181 next_prompt = content['next_prompt']
182 self._show_interpreter_prompt(next_prompt['prompt_number'],
182 self._show_interpreter_prompt(next_prompt['prompt_number'],
183 next_prompt['input_sep'])
183 next_prompt['input_sep'])
184
184
185 #---------------------------------------------------------------------------
185 #---------------------------------------------------------------------------
186 # 'IPythonWidget' interface
186 # 'IPythonWidget' interface
187 #---------------------------------------------------------------------------
187 #---------------------------------------------------------------------------
188
188
189 def edit(self, filename, line=None):
189 def edit(self, filename, line=None):
190 """ Opens a Python script for editing.
190 """ Opens a Python script for editing.
191
191
192 Parameters:
192 Parameters:
193 -----------
193 -----------
194 filename : str
194 filename : str
195 A path to a local system file.
195 A path to a local system file.
196
196
197 line : int, optional
197 line : int, optional
198 A line of interest in the file.
198 A line of interest in the file.
199
199
200 Raises:
200 Raises:
201 -------
201 -------
202 OSError
202 OSError
203 If the editor command cannot be executed.
203 If the editor command cannot be executed.
204 """
204 """
205 if self._editor == 'default':
205 if self._editor == 'default':
206 url = QtCore.QUrl.fromLocalFile(filename)
206 url = QtCore.QUrl.fromLocalFile(filename)
207 if not QtGui.QDesktopServices.openUrl(url):
207 if not QtGui.QDesktopServices.openUrl(url):
208 message = 'Failed to open %s with the default application'
208 message = 'Failed to open %s with the default application'
209 raise OSError(message % repr(filename))
209 raise OSError(message % repr(filename))
210 elif self._editor is None:
210 elif self._editor is None:
211 self.custom_edit_requested.emit(filename, line)
211 self.custom_edit_requested.emit(filename, line)
212 else:
212 else:
213 Popen(self._editor + [filename])
213 Popen(self._editor + [filename])
214
214
215 def reset_styling(self):
215 def reset_styling(self):
216 """ Restores the default IPythonWidget styling.
216 """ Restores the default IPythonWidget styling.
217 """
217 """
218 self.set_styling(self.default_stylesheet, syntax_style='default')
218 self.set_styling(self.default_stylesheet, syntax_style='default')
219 #self.set_styling(self.dark_stylesheet, syntax_style='monokai')
219 #self.set_styling(self.dark_stylesheet, syntax_style='monokai')
220
220
221 def set_editor(self, editor):
221 def set_editor(self, editor):
222 """ Sets the editor to use with the %edit magic.
222 """ Sets the editor to use with the %edit magic.
223
223
224 Parameters:
224 Parameters:
225 -----------
225 -----------
226 editor : str or sequence of str
226 editor : str or sequence of str
227 A command suitable for use with Popen. This command will be executed
227 A command suitable for use with Popen. This command will be executed
228 with a single argument--a filename--when editing is requested.
228 with a single argument--a filename--when editing is requested.
229
229
230 This parameter also takes two special values:
230 This parameter also takes two special values:
231 'default' : Files will be edited with the system default
231 'default' : Files will be edited with the system default
232 application for Python files.
232 application for Python files.
233 'custom' : Emit a 'custom_edit_requested(str, int)' signal
233 'custom' : Emit a 'custom_edit_requested(str, int)' signal
234 instead of opening an editor.
234 instead of opening an editor.
235 """
235 """
236 if editor == 'default':
236 if editor == 'default':
237 self._editor = 'default'
237 self._editor = 'default'
238 elif editor == 'custom':
238 elif editor == 'custom':
239 self._editor = None
239 self._editor = None
240 elif isinstance(editor, basestring):
240 elif isinstance(editor, basestring):
241 self._editor = [ editor ]
241 self._editor = [ editor ]
242 else:
242 else:
243 self._editor = list(editor)
243 self._editor = list(editor)
244
244
245 def set_styling(self, stylesheet, syntax_style=None):
245 def set_styling(self, stylesheet, syntax_style=None):
246 """ Sets the IPythonWidget styling.
246 """ Sets the IPythonWidget styling.
247
247
248 Parameters:
248 Parameters:
249 -----------
249 -----------
250 stylesheet : str
250 stylesheet : str
251 A CSS stylesheet. The stylesheet can contain classes for:
251 A CSS stylesheet. The stylesheet can contain classes for:
252 1. Qt: QPlainTextEdit, QFrame, QWidget, etc
252 1. Qt: QPlainTextEdit, QFrame, QWidget, etc
253 2. Pygments: .c, .k, .o, etc (see PygmentsHighlighter)
253 2. Pygments: .c, .k, .o, etc (see PygmentsHighlighter)
254 3. IPython: .error, .in-prompt, .out-prompt, etc.
254 3. IPython: .error, .in-prompt, .out-prompt, etc.
255
255
256 syntax_style : str or None [default None]
256 syntax_style : str or None [default None]
257 If specified, use the Pygments style with given name. Otherwise,
257 If specified, use the Pygments style with given name. Otherwise,
258 the stylesheet is queried for Pygments style information.
258 the stylesheet is queried for Pygments style information.
259 """
259 """
260 self.setStyleSheet(stylesheet)
260 self.setStyleSheet(stylesheet)
261 self._control.document().setDefaultStyleSheet(stylesheet)
261 self._control.document().setDefaultStyleSheet(stylesheet)
262 if self._page_control:
262 if self._page_control:
263 self._page_control.document().setDefaultStyleSheet(stylesheet)
263 self._page_control.document().setDefaultStyleSheet(stylesheet)
264
264
265 if syntax_style is None:
265 if syntax_style is None:
266 self._highlighter.set_style_sheet(stylesheet)
266 self._highlighter.set_style_sheet(stylesheet)
267 else:
267 else:
268 self._highlighter.set_style(syntax_style)
268 self._highlighter.set_style(syntax_style)
269
269
270 #---------------------------------------------------------------------------
270 #---------------------------------------------------------------------------
271 # 'IPythonWidget' protected interface
271 # 'IPythonWidget' protected interface
272 #---------------------------------------------------------------------------
272 #---------------------------------------------------------------------------
273
273
274 def _make_in_prompt(self, number):
274 def _make_in_prompt(self, number):
275 """ Given a prompt number, returns an HTML In prompt.
275 """ Given a prompt number, returns an HTML In prompt.
276 """
276 """
277 body = self.in_prompt % number
277 body = self.in_prompt % number
278 return '<span class="in-prompt">%s</span>' % body
278 return '<span class="in-prompt">%s</span>' % body
279
279
280 def _make_continuation_prompt(self, prompt):
280 def _make_continuation_prompt(self, prompt):
281 """ Given a plain text version of an In prompt, returns an HTML
281 """ Given a plain text version of an In prompt, returns an HTML
282 continuation prompt.
282 continuation prompt.
283 """
283 """
284 end_chars = '...: '
284 end_chars = '...: '
285 space_count = len(prompt.lstrip('\n')) - len(end_chars)
285 space_count = len(prompt.lstrip('\n')) - len(end_chars)
286 body = '&nbsp;' * space_count + end_chars
286 body = '&nbsp;' * space_count + end_chars
287 return '<span class="in-prompt">%s</span>' % body
287 return '<span class="in-prompt">%s</span>' % body
288
288
289 def _make_out_prompt(self, number):
289 def _make_out_prompt(self, number):
290 """ Given a prompt number, returns an HTML Out prompt.
290 """ Given a prompt number, returns an HTML Out prompt.
291 """
291 """
292 body = self.out_prompt % number
292 body = self.out_prompt % number
293 return '<span class="out-prompt">%s</span>' % body
293 return '<span class="out-prompt">%s</span>' % body
@@ -1,359 +1,365 b''
1 import inspect
1 import inspect
2 import re
2 import re
3 import sys
3 import sys
4 from subprocess import Popen, PIPE
4 from subprocess import Popen, PIPE
5
5
6 from IPython.core.interactiveshell import (
6 from IPython.core.interactiveshell import (
7 InteractiveShell, InteractiveShellABC
7 InteractiveShell, InteractiveShellABC
8 )
8 )
9 from IPython.core.displayhook import DisplayHook
9 from IPython.core.displayhook import DisplayHook
10 from IPython.core.macro import Macro
10 from IPython.core.macro import Macro
11 from IPython.utils.path import get_py_filename
11 from IPython.utils.path import get_py_filename
12 from IPython.utils.text import StringTypes
12 from IPython.utils.text import StringTypes
13 from IPython.utils.traitlets import Instance, Type, Dict
13 from IPython.utils.traitlets import Instance, Type, Dict
14 from IPython.utils.warn import warn
14 from IPython.utils.warn import warn
15 from IPython.zmq.session import extract_header
15 from IPython.zmq.session import extract_header
16 from IPython.core.payloadpage import install_payload_page
16 from IPython.core.payloadpage import install_payload_page
17
17
18
18
19 # Install the payload version of page.
19 # Install the payload version of page.
20 install_payload_page()
20 install_payload_page()
21
21
22
22
23 class ZMQDisplayHook(DisplayHook):
23 class ZMQDisplayHook(DisplayHook):
24
24
25 session = Instance('IPython.zmq.session.Session')
25 session = Instance('IPython.zmq.session.Session')
26 pub_socket = Instance('zmq.Socket')
26 pub_socket = Instance('zmq.Socket')
27 parent_header = Dict({})
27 parent_header = Dict({})
28
28
29 def set_parent(self, parent):
29 def set_parent(self, parent):
30 """Set the parent for outbound messages."""
30 """Set the parent for outbound messages."""
31 self.parent_header = extract_header(parent)
31 self.parent_header = extract_header(parent)
32
32
33 def start_displayhook(self):
33 def start_displayhook(self):
34 self.msg = self.session.msg(u'pyout', {}, parent=self.parent_header)
34 self.msg = self.session.msg(u'pyout', {}, parent=self.parent_header)
35
35
36 def write_output_prompt(self):
36 def write_output_prompt(self):
37 """Write the output prompt."""
37 """Write the output prompt."""
38 if self.do_full_cache:
38 if self.do_full_cache:
39 self.msg['content']['output_sep'] = self.output_sep
39 self.msg['content']['output_sep'] = self.output_sep
40 self.msg['content']['prompt_string'] = str(self.prompt_out)
40 self.msg['content']['prompt_string'] = str(self.prompt_out)
41 self.msg['content']['prompt_number'] = self.prompt_count
41 self.msg['content']['prompt_number'] = self.prompt_count
42 self.msg['content']['output_sep2'] = self.output_sep2
42 self.msg['content']['output_sep2'] = self.output_sep2
43
43
44 def write_result_repr(self, result_repr):
44 def write_result_repr(self, result_repr):
45 self.msg['content']['data'] = result_repr
45 self.msg['content']['data'] = result_repr
46
46
47 def finish_displayhook(self):
47 def finish_displayhook(self):
48 """Finish up all displayhook activities."""
48 """Finish up all displayhook activities."""
49 self.pub_socket.send_json(self.msg)
49 self.pub_socket.send_json(self.msg)
50 self.msg = None
50 self.msg = None
51
51
52
52
53 class ZMQInteractiveShell(InteractiveShell):
53 class ZMQInteractiveShell(InteractiveShell):
54 """A subclass of InteractiveShell for ZMQ."""
54 """A subclass of InteractiveShell for ZMQ."""
55
55
56 displayhook_class = Type(ZMQDisplayHook)
56 displayhook_class = Type(ZMQDisplayHook)
57
57
58 def system(self, cmd):
58 def system(self, cmd):
59 cmd = self.var_expand(cmd, depth=2)
59 cmd = self.var_expand(cmd, depth=2)
60 sys.stdout.flush()
60 sys.stdout.flush()
61 sys.stderr.flush()
61 sys.stderr.flush()
62 p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE)
62 p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE)
63 for line in p.stdout.read().split('\n'):
63 for line in p.stdout.read().split('\n'):
64 if len(line) > 0:
64 if len(line) > 0:
65 print line
65 print line
66 for line in p.stderr.read().split('\n'):
66 for line in p.stderr.read().split('\n'):
67 if len(line) > 0:
67 if len(line) > 0:
68 print line
68 print line
69 p.wait()
69 p.wait()
70
70
71 def init_io(self):
71 def init_io(self):
72 # This will just use sys.stdout and sys.stderr. If you want to
72 # This will just use sys.stdout and sys.stderr. If you want to
73 # override sys.stdout and sys.stderr themselves, you need to do that
73 # override sys.stdout and sys.stderr themselves, you need to do that
74 # *before* instantiating this class, because Term holds onto
74 # *before* instantiating this class, because Term holds onto
75 # references to the underlying streams.
75 # references to the underlying streams.
76 import IPython.utils.io
76 import IPython.utils.io
77 Term = IPython.utils.io.IOTerm()
77 Term = IPython.utils.io.IOTerm()
78 IPython.utils.io.Term = Term
78 IPython.utils.io.Term = Term
79
79
80 def magic_edit(self,parameter_s='',last_call=['','']):
80 def magic_edit(self,parameter_s='',last_call=['','']):
81 """Bring up an editor and execute the resulting code.
81 """Bring up an editor and execute the resulting code.
82
82
83 Usage:
83 Usage:
84 %edit [options] [args]
84 %edit [options] [args]
85
85
86 %edit runs IPython's editor hook. The default version of this hook is
86 %edit runs IPython's editor hook. The default version of this hook is
87 set to call the __IPYTHON__.rc.editor command. This is read from your
87 set to call the __IPYTHON__.rc.editor command. This is read from your
88 environment variable $EDITOR. If this isn't found, it will default to
88 environment variable $EDITOR. If this isn't found, it will default to
89 vi under Linux/Unix and to notepad under Windows. See the end of this
89 vi under Linux/Unix and to notepad under Windows. See the end of this
90 docstring for how to change the editor hook.
90 docstring for how to change the editor hook.
91
91
92 You can also set the value of this editor via the command line option
92 You can also set the value of this editor via the command line option
93 '-editor' or in your ipythonrc file. This is useful if you wish to use
93 '-editor' or in your ipythonrc file. This is useful if you wish to use
94 specifically for IPython an editor different from your typical default
94 specifically for IPython an editor different from your typical default
95 (and for Windows users who typically don't set environment variables).
95 (and for Windows users who typically don't set environment variables).
96
96
97 This command allows you to conveniently edit multi-line code right in
97 This command allows you to conveniently edit multi-line code right in
98 your IPython session.
98 your IPython session.
99
99
100 If called without arguments, %edit opens up an empty editor with a
100 If called without arguments, %edit opens up an empty editor with a
101 temporary file and will execute the contents of this file when you
101 temporary file and will execute the contents of this file when you
102 close it (don't forget to save it!).
102 close it (don't forget to save it!).
103
103
104
104
105 Options:
105 Options:
106
106
107 -n <number>: open the editor at a specified line number. By default,
107 -n <number>: open the editor at a specified line number. By default,
108 the IPython editor hook uses the unix syntax 'editor +N filename', but
108 the IPython editor hook uses the unix syntax 'editor +N filename', but
109 you can configure this by providing your own modified hook if your
109 you can configure this by providing your own modified hook if your
110 favorite editor supports line-number specifications with a different
110 favorite editor supports line-number specifications with a different
111 syntax.
111 syntax.
112
112
113 -p: this will call the editor with the same data as the previous time
113 -p: this will call the editor with the same data as the previous time
114 it was used, regardless of how long ago (in your current session) it
114 it was used, regardless of how long ago (in your current session) it
115 was.
115 was.
116
116
117 -r: use 'raw' input. This option only applies to input taken from the
117 -r: use 'raw' input. This option only applies to input taken from the
118 user's history. By default, the 'processed' history is used, so that
118 user's history. By default, the 'processed' history is used, so that
119 magics are loaded in their transformed version to valid Python. If
119 magics are loaded in their transformed version to valid Python. If
120 this option is given, the raw input as typed as the command line is
120 this option is given, the raw input as typed as the command line is
121 used instead. When you exit the editor, it will be executed by
121 used instead. When you exit the editor, it will be executed by
122 IPython's own processor.
122 IPython's own processor.
123
123
124 -x: do not execute the edited code immediately upon exit. This is
124 -x: do not execute the edited code immediately upon exit. This is
125 mainly useful if you are editing programs which need to be called with
125 mainly useful if you are editing programs which need to be called with
126 command line arguments, which you can then do using %run.
126 command line arguments, which you can then do using %run.
127
127
128
128
129 Arguments:
129 Arguments:
130
130
131 If arguments are given, the following possibilites exist:
131 If arguments are given, the following possibilites exist:
132
132
133 - The arguments are numbers or pairs of colon-separated numbers (like
133 - The arguments are numbers or pairs of colon-separated numbers (like
134 1 4:8 9). These are interpreted as lines of previous input to be
134 1 4:8 9). These are interpreted as lines of previous input to be
135 loaded into the editor. The syntax is the same of the %macro command.
135 loaded into the editor. The syntax is the same of the %macro command.
136
136
137 - If the argument doesn't start with a number, it is evaluated as a
137 - If the argument doesn't start with a number, it is evaluated as a
138 variable and its contents loaded into the editor. You can thus edit
138 variable and its contents loaded into the editor. You can thus edit
139 any string which contains python code (including the result of
139 any string which contains python code (including the result of
140 previous edits).
140 previous edits).
141
141
142 - If the argument is the name of an object (other than a string),
142 - If the argument is the name of an object (other than a string),
143 IPython will try to locate the file where it was defined and open the
143 IPython will try to locate the file where it was defined and open the
144 editor at the point where it is defined. You can use `%edit function`
144 editor at the point where it is defined. You can use `%edit function`
145 to load an editor exactly at the point where 'function' is defined,
145 to load an editor exactly at the point where 'function' is defined,
146 edit it and have the file be executed automatically.
146 edit it and have the file be executed automatically.
147
147
148 If the object is a macro (see %macro for details), this opens up your
148 If the object is a macro (see %macro for details), this opens up your
149 specified editor with a temporary file containing the macro's data.
149 specified editor with a temporary file containing the macro's data.
150 Upon exit, the macro is reloaded with the contents of the file.
150 Upon exit, the macro is reloaded with the contents of the file.
151
151
152 Note: opening at an exact line is only supported under Unix, and some
152 Note: opening at an exact line is only supported under Unix, and some
153 editors (like kedit and gedit up to Gnome 2.8) do not understand the
153 editors (like kedit and gedit up to Gnome 2.8) do not understand the
154 '+NUMBER' parameter necessary for this feature. Good editors like
154 '+NUMBER' parameter necessary for this feature. Good editors like
155 (X)Emacs, vi, jed, pico and joe all do.
155 (X)Emacs, vi, jed, pico and joe all do.
156
156
157 - If the argument is not found as a variable, IPython will look for a
157 - If the argument is not found as a variable, IPython will look for a
158 file with that name (adding .py if necessary) and load it into the
158 file with that name (adding .py if necessary) and load it into the
159 editor. It will execute its contents with execfile() when you exit,
159 editor. It will execute its contents with execfile() when you exit,
160 loading any code in the file into your interactive namespace.
160 loading any code in the file into your interactive namespace.
161
161
162 After executing your code, %edit will return as output the code you
162 After executing your code, %edit will return as output the code you
163 typed in the editor (except when it was an existing file). This way
163 typed in the editor (except when it was an existing file). This way
164 you can reload the code in further invocations of %edit as a variable,
164 you can reload the code in further invocations of %edit as a variable,
165 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
165 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
166 the output.
166 the output.
167
167
168 Note that %edit is also available through the alias %ed.
168 Note that %edit is also available through the alias %ed.
169
169
170 This is an example of creating a simple function inside the editor and
170 This is an example of creating a simple function inside the editor and
171 then modifying it. First, start up the editor:
171 then modifying it. First, start up the editor:
172
172
173 In [1]: ed
173 In [1]: ed
174 Editing... done. Executing edited code...
174 Editing... done. Executing edited code...
175 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
175 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
176
176
177 We can then call the function foo():
177 We can then call the function foo():
178
178
179 In [2]: foo()
179 In [2]: foo()
180 foo() was defined in an editing session
180 foo() was defined in an editing session
181
181
182 Now we edit foo. IPython automatically loads the editor with the
182 Now we edit foo. IPython automatically loads the editor with the
183 (temporary) file where foo() was previously defined:
183 (temporary) file where foo() was previously defined:
184
184
185 In [3]: ed foo
185 In [3]: ed foo
186 Editing... done. Executing edited code...
186 Editing... done. Executing edited code...
187
187
188 And if we call foo() again we get the modified version:
188 And if we call foo() again we get the modified version:
189
189
190 In [4]: foo()
190 In [4]: foo()
191 foo() has now been changed!
191 foo() has now been changed!
192
192
193 Here is an example of how to edit a code snippet successive
193 Here is an example of how to edit a code snippet successive
194 times. First we call the editor:
194 times. First we call the editor:
195
195
196 In [5]: ed
196 In [5]: ed
197 Editing... done. Executing edited code...
197 Editing... done. Executing edited code...
198 hello
198 hello
199 Out[5]: "print 'hello'n"
199 Out[5]: "print 'hello'n"
200
200
201 Now we call it again with the previous output (stored in _):
201 Now we call it again with the previous output (stored in _):
202
202
203 In [6]: ed _
203 In [6]: ed _
204 Editing... done. Executing edited code...
204 Editing... done. Executing edited code...
205 hello world
205 hello world
206 Out[6]: "print 'hello world'n"
206 Out[6]: "print 'hello world'n"
207
207
208 Now we call it with the output #8 (stored in _8, also as Out[8]):
208 Now we call it with the output #8 (stored in _8, also as Out[8]):
209
209
210 In [7]: ed _8
210 In [7]: ed _8
211 Editing... done. Executing edited code...
211 Editing... done. Executing edited code...
212 hello again
212 hello again
213 Out[7]: "print 'hello again'n"
213 Out[7]: "print 'hello again'n"
214
214
215
215
216 Changing the default editor hook:
216 Changing the default editor hook:
217
217
218 If you wish to write your own editor hook, you can put it in a
218 If you wish to write your own editor hook, you can put it in a
219 configuration file which you load at startup time. The default hook
219 configuration file which you load at startup time. The default hook
220 is defined in the IPython.core.hooks module, and you can use that as a
220 is defined in the IPython.core.hooks module, and you can use that as a
221 starting example for further modifications. That file also has
221 starting example for further modifications. That file also has
222 general instructions on how to set a new hook for use once you've
222 general instructions on how to set a new hook for use once you've
223 defined it."""
223 defined it."""
224
224
225 # FIXME: This function has become a convoluted mess. It needs a
225 # FIXME: This function has become a convoluted mess. It needs a
226 # ground-up rewrite with clean, simple logic.
226 # ground-up rewrite with clean, simple logic.
227
227
228 def make_filename(arg):
228 def make_filename(arg):
229 "Make a filename from the given args"
229 "Make a filename from the given args"
230 try:
230 try:
231 filename = get_py_filename(arg)
231 filename = get_py_filename(arg)
232 except IOError:
232 except IOError:
233 if args.endswith('.py'):
233 if args.endswith('.py'):
234 filename = arg
234 filename = arg
235 else:
235 else:
236 filename = None
236 filename = None
237 return filename
237 return filename
238
238
239 # custom exceptions
239 # custom exceptions
240 class DataIsObject(Exception): pass
240 class DataIsObject(Exception): pass
241
241
242 opts,args = self.parse_options(parameter_s,'prn:')
242 opts,args = self.parse_options(parameter_s,'prn:')
243 # Set a few locals from the options for convenience:
243 # Set a few locals from the options for convenience:
244 opts_p = opts.has_key('p')
244 opts_p = opts.has_key('p')
245 opts_r = opts.has_key('r')
245 opts_r = opts.has_key('r')
246
246
247 # Default line number value
247 # Default line number value
248 lineno = opts.get('n',None)
248 lineno = opts.get('n',None)
249 if lineno is not None:
250 try:
251 lineno = int(lineno)
252 except:
253 warn("The -n argument must be an integer.")
254 return
249
255
250 if opts_p:
256 if opts_p:
251 args = '_%s' % last_call[0]
257 args = '_%s' % last_call[0]
252 if not self.shell.user_ns.has_key(args):
258 if not self.shell.user_ns.has_key(args):
253 args = last_call[1]
259 args = last_call[1]
254
260
255 # use last_call to remember the state of the previous call, but don't
261 # use last_call to remember the state of the previous call, but don't
256 # let it be clobbered by successive '-p' calls.
262 # let it be clobbered by successive '-p' calls.
257 try:
263 try:
258 last_call[0] = self.shell.displayhook.prompt_count
264 last_call[0] = self.shell.displayhook.prompt_count
259 if not opts_p:
265 if not opts_p:
260 last_call[1] = parameter_s
266 last_call[1] = parameter_s
261 except:
267 except:
262 pass
268 pass
263
269
264 # by default this is done with temp files, except when the given
270 # by default this is done with temp files, except when the given
265 # arg is a filename
271 # arg is a filename
266 use_temp = 1
272 use_temp = 1
267
273
268 if re.match(r'\d',args):
274 if re.match(r'\d',args):
269 # Mode where user specifies ranges of lines, like in %macro.
275 # Mode where user specifies ranges of lines, like in %macro.
270 # This means that you can't edit files whose names begin with
276 # This means that you can't edit files whose names begin with
271 # numbers this way. Tough.
277 # numbers this way. Tough.
272 ranges = args.split()
278 ranges = args.split()
273 data = ''.join(self.extract_input_slices(ranges,opts_r))
279 data = ''.join(self.extract_input_slices(ranges,opts_r))
274 elif args.endswith('.py'):
280 elif args.endswith('.py'):
275 filename = make_filename(args)
281 filename = make_filename(args)
276 data = ''
282 data = ''
277 use_temp = 0
283 use_temp = 0
278 elif args:
284 elif args:
279 try:
285 try:
280 # Load the parameter given as a variable. If not a string,
286 # Load the parameter given as a variable. If not a string,
281 # process it as an object instead (below)
287 # process it as an object instead (below)
282
288
283 #print '*** args',args,'type',type(args) # dbg
289 #print '*** args',args,'type',type(args) # dbg
284 data = eval(args,self.shell.user_ns)
290 data = eval(args,self.shell.user_ns)
285 if not type(data) in StringTypes:
291 if not type(data) in StringTypes:
286 raise DataIsObject
292 raise DataIsObject
287
293
288 except (NameError,SyntaxError):
294 except (NameError,SyntaxError):
289 # given argument is not a variable, try as a filename
295 # given argument is not a variable, try as a filename
290 filename = make_filename(args)
296 filename = make_filename(args)
291 if filename is None:
297 if filename is None:
292 warn("Argument given (%s) can't be found as a variable "
298 warn("Argument given (%s) can't be found as a variable "
293 "or as a filename." % args)
299 "or as a filename." % args)
294 return
300 return
295
301
296 data = ''
302 data = ''
297 use_temp = 0
303 use_temp = 0
298 except DataIsObject:
304 except DataIsObject:
299
305
300 # macros have a special edit function
306 # macros have a special edit function
301 if isinstance(data,Macro):
307 if isinstance(data,Macro):
302 self._edit_macro(args,data)
308 self._edit_macro(args,data)
303 return
309 return
304
310
305 # For objects, try to edit the file where they are defined
311 # For objects, try to edit the file where they are defined
306 try:
312 try:
307 filename = inspect.getabsfile(data)
313 filename = inspect.getabsfile(data)
308 if 'fakemodule' in filename.lower() and inspect.isclass(data):
314 if 'fakemodule' in filename.lower() and inspect.isclass(data):
309 # class created by %edit? Try to find source
315 # class created by %edit? Try to find source
310 # by looking for method definitions instead, the
316 # by looking for method definitions instead, the
311 # __module__ in those classes is FakeModule.
317 # __module__ in those classes is FakeModule.
312 attrs = [getattr(data, aname) for aname in dir(data)]
318 attrs = [getattr(data, aname) for aname in dir(data)]
313 for attr in attrs:
319 for attr in attrs:
314 if not inspect.ismethod(attr):
320 if not inspect.ismethod(attr):
315 continue
321 continue
316 filename = inspect.getabsfile(attr)
322 filename = inspect.getabsfile(attr)
317 if filename and 'fakemodule' not in filename.lower():
323 if filename and 'fakemodule' not in filename.lower():
318 # change the attribute to be the edit target instead
324 # change the attribute to be the edit target instead
319 data = attr
325 data = attr
320 break
326 break
321
327
322 datafile = 1
328 datafile = 1
323 except TypeError:
329 except TypeError:
324 filename = make_filename(args)
330 filename = make_filename(args)
325 datafile = 1
331 datafile = 1
326 warn('Could not find file where `%s` is defined.\n'
332 warn('Could not find file where `%s` is defined.\n'
327 'Opening a file named `%s`' % (args,filename))
333 'Opening a file named `%s`' % (args,filename))
328 # Now, make sure we can actually read the source (if it was in
334 # Now, make sure we can actually read the source (if it was in
329 # a temp file it's gone by now).
335 # a temp file it's gone by now).
330 if datafile:
336 if datafile:
331 try:
337 try:
332 if lineno is None:
338 if lineno is None:
333 lineno = inspect.getsourcelines(data)[1]
339 lineno = inspect.getsourcelines(data)[1]
334 except IOError:
340 except IOError:
335 filename = make_filename(args)
341 filename = make_filename(args)
336 if filename is None:
342 if filename is None:
337 warn('The file `%s` where `%s` was defined cannot '
343 warn('The file `%s` where `%s` was defined cannot '
338 'be read.' % (filename,data))
344 'be read.' % (filename,data))
339 return
345 return
340 use_temp = 0
346 use_temp = 0
341 else:
347 else:
342 data = ''
348 data = ''
343
349
344 if use_temp:
350 if use_temp:
345 filename = self.shell.mktempfile(data)
351 filename = self.shell.mktempfile(data)
346 print 'IPython will make a temporary file named:',filename
352 print 'IPython will make a temporary file named:',filename
347
353
348 payload = {
354 payload = {
349 'source' : 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic',
355 'source' : 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic',
350 'filename' : filename,
356 'filename' : filename,
351 'line_number' : lineno
357 'line_number' : lineno
352 }
358 }
353 self.payload_manager.write_payload(payload)
359 self.payload_manager.write_payload(payload)
354
360
355
361
356 InteractiveShellABC.register(ZMQInteractiveShell)
362 InteractiveShellABC.register(ZMQInteractiveShell)
357
363
358
364
359
365
General Comments 0
You need to be logged in to leave comments. Login now