##// END OF EJS Templates
Provide html support for page() in the payload version....
Fernando Perez -
Show More
@@ -1,50 +1,97 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 A payload based version of page.
4 A payload based version of page.
5
5
6 Authors:
6 Authors:
7
7
8 * Brian Granger
8 * Brian Granger
9 * Fernando Perez
9 * Fernando Perez
10 """
10 """
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Copyright (C) 2008-2010 The IPython Development Team
13 # Copyright (C) 2008-2010 The IPython Development Team
14 #
14 #
15 # Distributed under the terms of the BSD License. The full license is in
15 # Distributed under the terms of the BSD License. The full license is in
16 # the file COPYING, distributed as part of this software.
16 # the file COPYING, distributed as part of this software.
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Imports
20 # Imports
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22
22
23 # Third-party
24 try:
25 from docutils.core import publish_string
26 except ImportError:
27 # html paging won't be available, but we don't raise any errors. It's a
28 # purely optional feature.
29 pass
30
31 # Our own
23 from IPython.core.interactiveshell import InteractiveShell
32 from IPython.core.interactiveshell import InteractiveShell
24
33
25 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
26 # Classes and functions
35 # Classes and functions
27 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
28
37
29 def page(strng, start=0, screen_lines=0, pager_cmd=None):
38 def page(strng, start=0, screen_lines=0, pager_cmd=None,
39 html=None, auto_html=False):
30 """Print a string, piping through a pager.
40 """Print a string, piping through a pager.
31
41
32 This version ignores the screen_lines and pager_cmd arguments and uses
42 This version ignores the screen_lines and pager_cmd arguments and uses
33 IPython's payload system instead.
43 IPython's payload system instead.
44
45 Parameters
46 ----------
47 strng : str
48 Text to page.
49
50 start : int
51 Starting line at which to place the display.
52
53 html : str, optional
54 If given, an html string to send as well.
55
56 auto_html : bool, optional
57 If true, the input string is assumed to be valid reStructuredText and is
58 converted to HTML with docutils. Note that if docutils is not found,
59 this option is silently ignored.
60
61 Note
62 ----
63
64 Only one of the ``html`` and ``auto_html`` options can be given, not
65 both.
34 """
66 """
35
67
36 # Some routines may auto-compute start offsets incorrectly and pass a
68 # Some routines may auto-compute start offsets incorrectly and pass a
37 # negative value. Offset to 0 for robustness.
69 # negative value. Offset to 0 for robustness.
38 start = max(0, start)
70 start = max(0, start)
39 shell = InteractiveShell.instance()
71 shell = InteractiveShell.instance()
72
73 if auto_html:
74 try:
75 # These defaults ensure user configuration variables for docutils
76 # are not loaded, only our config is used here.
77 defaults = {'file_insertion_enabled': 0,
78 'raw_enabled': 0,
79 '_disable_config': 1}
80 html = publish_string(strng, writer_name='html',
81 settings_overrides=defaults)
82 except:
83 pass
84
40 payload = dict(
85 payload = dict(
41 source='IPython.zmq.page.page',
86 source='IPython.zmq.page.page',
42 data=strng,
87 text=strng,
88 html=html,
43 start_line_number=start
89 start_line_number=start
44 )
90 )
45 shell.payload_manager.write_payload(payload)
91 shell.payload_manager.write_payload(payload)
46
92
93
47 def install_payload_page():
94 def install_payload_page():
48 """Install this version of page as IPython.core.page.page."""
95 """Install this version of page as IPython.core.page.page."""
49 from IPython.core import page as corepage
96 from IPython.core import page as corepage
50 corepage.page = page
97 corepage.page = page
@@ -1,455 +1,455 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
17
18 # System library imports
18 # System library imports
19 from PyQt4 import QtCore, QtGui
19 from PyQt4 import QtCore, QtGui
20
20
21 # Local imports
21 # Local imports
22 from IPython.core.inputsplitter import IPythonInputSplitter, \
22 from IPython.core.inputsplitter import IPythonInputSplitter, \
23 transform_ipy_prompt
23 transform_ipy_prompt
24 from IPython.core.usage import default_gui_banner
24 from IPython.core.usage import default_gui_banner
25 from IPython.utils.traitlets import Bool, Str
25 from IPython.utils.traitlets import Bool, Str
26 from frontend_widget import FrontendWidget
26 from frontend_widget import FrontendWidget
27
27
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29 # Constants
29 # Constants
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31
31
32 # The default light style sheet: black text on a white background.
32 # The default light style sheet: black text on a white background.
33 default_light_style_sheet = '''
33 default_light_style_sheet = '''
34 .error { color: red; }
34 .error { color: red; }
35 .in-prompt { color: navy; }
35 .in-prompt { color: navy; }
36 .in-prompt-number { font-weight: bold; }
36 .in-prompt-number { font-weight: bold; }
37 .out-prompt { color: darkred; }
37 .out-prompt { color: darkred; }
38 .out-prompt-number { font-weight: bold; }
38 .out-prompt-number { font-weight: bold; }
39 '''
39 '''
40 default_light_syntax_style = 'default'
40 default_light_syntax_style = 'default'
41
41
42 # The default dark style sheet: white text on a black background.
42 # The default dark style sheet: white text on a black background.
43 default_dark_style_sheet = '''
43 default_dark_style_sheet = '''
44 QPlainTextEdit, QTextEdit { background-color: black; color: white }
44 QPlainTextEdit, QTextEdit { background-color: black; color: white }
45 QFrame { border: 1px solid grey; }
45 QFrame { border: 1px solid grey; }
46 .error { color: red; }
46 .error { color: red; }
47 .in-prompt { color: lime; }
47 .in-prompt { color: lime; }
48 .in-prompt-number { color: lime; font-weight: bold; }
48 .in-prompt-number { color: lime; font-weight: bold; }
49 .out-prompt { color: red; }
49 .out-prompt { color: red; }
50 .out-prompt-number { color: red; font-weight: bold; }
50 .out-prompt-number { color: red; font-weight: bold; }
51 '''
51 '''
52 default_dark_syntax_style = 'monokai'
52 default_dark_syntax_style = 'monokai'
53
53
54 # Default strings to build and display input and output prompts (and separators
54 # Default strings to build and display input and output prompts (and separators
55 # in between)
55 # in between)
56 default_in_prompt = 'In [<span class="in-prompt-number">%i</span>]: '
56 default_in_prompt = 'In [<span class="in-prompt-number">%i</span>]: '
57 default_out_prompt = 'Out[<span class="out-prompt-number">%i</span>]: '
57 default_out_prompt = 'Out[<span class="out-prompt-number">%i</span>]: '
58 default_input_sep = '\n'
58 default_input_sep = '\n'
59 default_output_sep = ''
59 default_output_sep = ''
60 default_output_sep2 = ''
60 default_output_sep2 = ''
61
61
62 #-----------------------------------------------------------------------------
62 #-----------------------------------------------------------------------------
63 # IPythonWidget class
63 # IPythonWidget class
64 #-----------------------------------------------------------------------------
64 #-----------------------------------------------------------------------------
65
65
66 class IPythonWidget(FrontendWidget):
66 class IPythonWidget(FrontendWidget):
67 """ A FrontendWidget for an IPython kernel.
67 """ A FrontendWidget for an IPython kernel.
68 """
68 """
69
69
70 # If set, the 'custom_edit_requested(str, int)' signal will be emitted when
70 # 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'
71 # an editor is needed for a file. This overrides 'editor' and 'editor_line'
72 # settings.
72 # settings.
73 custom_edit = Bool(False)
73 custom_edit = Bool(False)
74 custom_edit_requested = QtCore.pyqtSignal(object, object)
74 custom_edit_requested = QtCore.pyqtSignal(object, object)
75
75
76 # A command for invoking a system text editor. If the string contains a
76 # 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
77 # {filename} format specifier, it will be used. Otherwise, the filename will
78 # be appended to the end the command.
78 # be appended to the end the command.
79 editor = Str('default', config=True)
79 editor = Str('default', config=True)
80
80
81 # The editor command to use when a specific line number is requested. The
81 # The editor command to use when a specific line number is requested. The
82 # string should contain two format specifiers: {line} and {filename}. If
82 # string should contain two format specifiers: {line} and {filename}. If
83 # this parameter is not specified, the line number option to the %edit magic
83 # this parameter is not specified, the line number option to the %edit magic
84 # will be ignored.
84 # will be ignored.
85 editor_line = Str(config=True)
85 editor_line = Str(config=True)
86
86
87 # A CSS stylesheet. The stylesheet can contain classes for:
87 # A CSS stylesheet. The stylesheet can contain classes for:
88 # 1. Qt: QPlainTextEdit, QFrame, QWidget, etc
88 # 1. Qt: QPlainTextEdit, QFrame, QWidget, etc
89 # 2. Pygments: .c, .k, .o, etc (see PygmentsHighlighter)
89 # 2. Pygments: .c, .k, .o, etc (see PygmentsHighlighter)
90 # 3. IPython: .error, .in-prompt, .out-prompt, etc
90 # 3. IPython: .error, .in-prompt, .out-prompt, etc
91 style_sheet = Str(config=True)
91 style_sheet = Str(config=True)
92
92
93 # If not empty, use this Pygments style for syntax highlighting. Otherwise,
93 # If not empty, use this Pygments style for syntax highlighting. Otherwise,
94 # the style sheet is queried for Pygments style information.
94 # the style sheet is queried for Pygments style information.
95 syntax_style = Str(config=True)
95 syntax_style = Str(config=True)
96
96
97 # Prompts.
97 # Prompts.
98 in_prompt = Str(default_in_prompt, config=True)
98 in_prompt = Str(default_in_prompt, config=True)
99 out_prompt = Str(default_out_prompt, config=True)
99 out_prompt = Str(default_out_prompt, config=True)
100 input_sep = Str(default_input_sep, config=True)
100 input_sep = Str(default_input_sep, config=True)
101 output_sep = Str(default_output_sep, config=True)
101 output_sep = Str(default_output_sep, config=True)
102 output_sep2 = Str(default_output_sep2, config=True)
102 output_sep2 = Str(default_output_sep2, config=True)
103
103
104 # FrontendWidget protected class variables.
104 # FrontendWidget protected class variables.
105 _input_splitter_class = IPythonInputSplitter
105 _input_splitter_class = IPythonInputSplitter
106
106
107 # IPythonWidget protected class variables.
107 # IPythonWidget protected class variables.
108 _PromptBlock = namedtuple('_PromptBlock', ['block', 'length', 'number'])
108 _PromptBlock = namedtuple('_PromptBlock', ['block', 'length', 'number'])
109 _payload_source_edit = 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic'
109 _payload_source_edit = 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic'
110 _payload_source_exit = 'IPython.zmq.zmqshell.ZMQInteractiveShell.ask_exit'
110 _payload_source_exit = 'IPython.zmq.zmqshell.ZMQInteractiveShell.ask_exit'
111 _payload_source_page = 'IPython.zmq.page.page'
111 _payload_source_page = 'IPython.zmq.page.page'
112
112
113 #---------------------------------------------------------------------------
113 #---------------------------------------------------------------------------
114 # 'object' interface
114 # 'object' interface
115 #---------------------------------------------------------------------------
115 #---------------------------------------------------------------------------
116
116
117 def __init__(self, *args, **kw):
117 def __init__(self, *args, **kw):
118 super(IPythonWidget, self).__init__(*args, **kw)
118 super(IPythonWidget, self).__init__(*args, **kw)
119
119
120 # IPythonWidget protected variables.
120 # IPythonWidget protected variables.
121 self._payload_handlers = {
121 self._payload_handlers = {
122 self._payload_source_edit : self._handle_payload_edit,
122 self._payload_source_edit : self._handle_payload_edit,
123 self._payload_source_exit : self._handle_payload_exit,
123 self._payload_source_exit : self._handle_payload_exit,
124 self._payload_source_page : self._handle_payload_page }
124 self._payload_source_page : self._handle_payload_page }
125 self._previous_prompt_obj = None
125 self._previous_prompt_obj = None
126
126
127 # Initialize widget styling.
127 # Initialize widget styling.
128 if self.style_sheet:
128 if self.style_sheet:
129 self._style_sheet_changed()
129 self._style_sheet_changed()
130 self._syntax_style_changed()
130 self._syntax_style_changed()
131 else:
131 else:
132 self.set_default_style()
132 self.set_default_style()
133
133
134 #---------------------------------------------------------------------------
134 #---------------------------------------------------------------------------
135 # 'BaseFrontendMixin' abstract interface
135 # 'BaseFrontendMixin' abstract interface
136 #---------------------------------------------------------------------------
136 #---------------------------------------------------------------------------
137
137
138 def _handle_complete_reply(self, rep):
138 def _handle_complete_reply(self, rep):
139 """ Reimplemented to support IPython's improved completion machinery.
139 """ Reimplemented to support IPython's improved completion machinery.
140 """
140 """
141 cursor = self._get_cursor()
141 cursor = self._get_cursor()
142 info = self._request_info.get('complete')
142 info = self._request_info.get('complete')
143 if info and info.id == rep['parent_header']['msg_id'] and \
143 if info and info.id == rep['parent_header']['msg_id'] and \
144 info.pos == cursor.position():
144 info.pos == cursor.position():
145 matches = rep['content']['matches']
145 matches = rep['content']['matches']
146 text = rep['content']['matched_text']
146 text = rep['content']['matched_text']
147 offset = len(text)
147 offset = len(text)
148
148
149 # Clean up matches with period and path separators if the matched
149 # Clean up matches with period and path separators if the matched
150 # text has not been transformed. This is done by truncating all
150 # text has not been transformed. This is done by truncating all
151 # but the last component and then suitably decreasing the offset
151 # but the last component and then suitably decreasing the offset
152 # between the current cursor position and the start of completion.
152 # between the current cursor position and the start of completion.
153 if len(matches) > 1 and matches[0][:offset] == text:
153 if len(matches) > 1 and matches[0][:offset] == text:
154 parts = re.split(r'[./\\]', text)
154 parts = re.split(r'[./\\]', text)
155 sep_count = len(parts) - 1
155 sep_count = len(parts) - 1
156 if sep_count:
156 if sep_count:
157 chop_length = sum(map(len, parts[:sep_count])) + sep_count
157 chop_length = sum(map(len, parts[:sep_count])) + sep_count
158 matches = [ match[chop_length:] for match in matches ]
158 matches = [ match[chop_length:] for match in matches ]
159 offset -= chop_length
159 offset -= chop_length
160
160
161 # Move the cursor to the start of the match and complete.
161 # Move the cursor to the start of the match and complete.
162 cursor.movePosition(QtGui.QTextCursor.Left, n=offset)
162 cursor.movePosition(QtGui.QTextCursor.Left, n=offset)
163 self._complete_with_items(cursor, matches)
163 self._complete_with_items(cursor, matches)
164
164
165 def _handle_execute_reply(self, msg):
165 def _handle_execute_reply(self, msg):
166 """ Reimplemented to support prompt requests.
166 """ Reimplemented to support prompt requests.
167 """
167 """
168 info = self._request_info.get('execute')
168 info = self._request_info.get('execute')
169 if info and info.id == msg['parent_header']['msg_id']:
169 if info and info.id == msg['parent_header']['msg_id']:
170 if info.kind == 'prompt':
170 if info.kind == 'prompt':
171 number = msg['content']['execution_count'] + 1
171 number = msg['content']['execution_count'] + 1
172 self._show_interpreter_prompt(number)
172 self._show_interpreter_prompt(number)
173 else:
173 else:
174 super(IPythonWidget, self)._handle_execute_reply(msg)
174 super(IPythonWidget, self)._handle_execute_reply(msg)
175
175
176 def _handle_history_reply(self, msg):
176 def _handle_history_reply(self, msg):
177 """ Implemented to handle history replies, which are only supported by
177 """ Implemented to handle history replies, which are only supported by
178 the IPython kernel.
178 the IPython kernel.
179 """
179 """
180 history_dict = msg['content']['history']
180 history_dict = msg['content']['history']
181 items = [ history_dict[key] for key in sorted(history_dict.keys()) ]
181 items = [ history_dict[key] for key in sorted(history_dict.keys()) ]
182 self._set_history(items)
182 self._set_history(items)
183
183
184 def _handle_pyout(self, msg):
184 def _handle_pyout(self, msg):
185 """ Reimplemented for IPython-style "display hook".
185 """ Reimplemented for IPython-style "display hook".
186 """
186 """
187 if not self._hidden and self._is_from_this_session(msg):
187 if not self._hidden and self._is_from_this_session(msg):
188 content = msg['content']
188 content = msg['content']
189 prompt_number = content['execution_count']
189 prompt_number = content['execution_count']
190 self._append_plain_text(self.output_sep)
190 self._append_plain_text(self.output_sep)
191 self._append_html(self._make_out_prompt(prompt_number))
191 self._append_html(self._make_out_prompt(prompt_number))
192 self._append_plain_text(content['data']+self.output_sep2)
192 self._append_plain_text(content['data']+self.output_sep2)
193
193
194 def _started_channels(self):
194 def _started_channels(self):
195 """ Reimplemented to make a history request.
195 """ Reimplemented to make a history request.
196 """
196 """
197 super(IPythonWidget, self)._started_channels()
197 super(IPythonWidget, self)._started_channels()
198 # FIXME: Disabled until history requests are properly implemented.
198 # FIXME: Disabled until history requests are properly implemented.
199 #self.kernel_manager.xreq_channel.history(raw=True, output=False)
199 #self.kernel_manager.xreq_channel.history(raw=True, output=False)
200
200
201 #---------------------------------------------------------------------------
201 #---------------------------------------------------------------------------
202 # 'ConsoleWidget' public interface
202 # 'ConsoleWidget' public interface
203 #---------------------------------------------------------------------------
203 #---------------------------------------------------------------------------
204
204
205 def copy(self):
205 def copy(self):
206 """ Copy the currently selected text to the clipboard, removing prompts
206 """ Copy the currently selected text to the clipboard, removing prompts
207 if possible.
207 if possible.
208 """
208 """
209 text = str(self._control.textCursor().selection().toPlainText())
209 text = str(self._control.textCursor().selection().toPlainText())
210 if text:
210 if text:
211 # Remove prompts.
211 # Remove prompts.
212 lines = map(transform_ipy_prompt, text.splitlines())
212 lines = map(transform_ipy_prompt, text.splitlines())
213 text = '\n'.join(lines)
213 text = '\n'.join(lines)
214 # Expand tabs so that we respect PEP-8.
214 # Expand tabs so that we respect PEP-8.
215 QtGui.QApplication.clipboard().setText(text.expandtabs(4))
215 QtGui.QApplication.clipboard().setText(text.expandtabs(4))
216
216
217 #---------------------------------------------------------------------------
217 #---------------------------------------------------------------------------
218 # 'FrontendWidget' public interface
218 # 'FrontendWidget' public interface
219 #---------------------------------------------------------------------------
219 #---------------------------------------------------------------------------
220
220
221 def execute_file(self, path, hidden=False):
221 def execute_file(self, path, hidden=False):
222 """ Reimplemented to use the 'run' magic.
222 """ Reimplemented to use the 'run' magic.
223 """
223 """
224 self.execute('%%run %s' % path, hidden=hidden)
224 self.execute('%%run %s' % path, hidden=hidden)
225
225
226 #---------------------------------------------------------------------------
226 #---------------------------------------------------------------------------
227 # 'FrontendWidget' protected interface
227 # 'FrontendWidget' protected interface
228 #---------------------------------------------------------------------------
228 #---------------------------------------------------------------------------
229
229
230 def _complete(self):
230 def _complete(self):
231 """ Reimplemented to support IPython's improved completion machinery.
231 """ Reimplemented to support IPython's improved completion machinery.
232 """
232 """
233 # We let the kernel split the input line, so we *always* send an empty
233 # We let the kernel split the input line, so we *always* send an empty
234 # text field. Readline-based frontends do get a real text field which
234 # text field. Readline-based frontends do get a real text field which
235 # they can use.
235 # they can use.
236 text = ''
236 text = ''
237
237
238 # Send the completion request to the kernel
238 # Send the completion request to the kernel
239 msg_id = self.kernel_manager.xreq_channel.complete(
239 msg_id = self.kernel_manager.xreq_channel.complete(
240 text, # text
240 text, # text
241 self._get_input_buffer_cursor_line(), # line
241 self._get_input_buffer_cursor_line(), # line
242 self._get_input_buffer_cursor_column(), # cursor_pos
242 self._get_input_buffer_cursor_column(), # cursor_pos
243 self.input_buffer) # block
243 self.input_buffer) # block
244 pos = self._get_cursor().position()
244 pos = self._get_cursor().position()
245 info = self._CompletionRequest(msg_id, pos)
245 info = self._CompletionRequest(msg_id, pos)
246 self._request_info['complete'] = info
246 self._request_info['complete'] = info
247
247
248 def _get_banner(self):
248 def _get_banner(self):
249 """ Reimplemented to return IPython's default banner.
249 """ Reimplemented to return IPython's default banner.
250 """
250 """
251 return default_gui_banner
251 return default_gui_banner
252
252
253 def _process_execute_error(self, msg):
253 def _process_execute_error(self, msg):
254 """ Reimplemented for IPython-style traceback formatting.
254 """ Reimplemented for IPython-style traceback formatting.
255 """
255 """
256 content = msg['content']
256 content = msg['content']
257 traceback = '\n'.join(content['traceback']) + '\n'
257 traceback = '\n'.join(content['traceback']) + '\n'
258 if False:
258 if False:
259 # FIXME: For now, tracebacks come as plain text, so we can't use
259 # FIXME: For now, tracebacks come as plain text, so we can't use
260 # the html renderer yet. Once we refactor ultratb to produce
260 # the html renderer yet. Once we refactor ultratb to produce
261 # properly styled tracebacks, this branch should be the default
261 # properly styled tracebacks, this branch should be the default
262 traceback = traceback.replace(' ', '&nbsp;')
262 traceback = traceback.replace(' ', '&nbsp;')
263 traceback = traceback.replace('\n', '<br/>')
263 traceback = traceback.replace('\n', '<br/>')
264
264
265 ename = content['ename']
265 ename = content['ename']
266 ename_styled = '<span class="error">%s</span>' % ename
266 ename_styled = '<span class="error">%s</span>' % ename
267 traceback = traceback.replace(ename, ename_styled)
267 traceback = traceback.replace(ename, ename_styled)
268
268
269 self._append_html(traceback)
269 self._append_html(traceback)
270 else:
270 else:
271 # This is the fallback for now, using plain text with ansi escapes
271 # This is the fallback for now, using plain text with ansi escapes
272 self._append_plain_text(traceback)
272 self._append_plain_text(traceback)
273
273
274 def _process_execute_payload(self, item):
274 def _process_execute_payload(self, item):
275 """ Reimplemented to dispatch payloads to handler methods.
275 """ Reimplemented to dispatch payloads to handler methods.
276 """
276 """
277 handler = self._payload_handlers.get(item['source'])
277 handler = self._payload_handlers.get(item['source'])
278 if handler is None:
278 if handler is None:
279 # We have no handler for this type of payload, simply ignore it
279 # We have no handler for this type of payload, simply ignore it
280 return False
280 return False
281 else:
281 else:
282 handler(item)
282 handler(item)
283 return True
283 return True
284
284
285 def _show_interpreter_prompt(self, number=None):
285 def _show_interpreter_prompt(self, number=None):
286 """ Reimplemented for IPython-style prompts.
286 """ Reimplemented for IPython-style prompts.
287 """
287 """
288 # If a number was not specified, make a prompt number request.
288 # If a number was not specified, make a prompt number request.
289 if number is None:
289 if number is None:
290 msg_id = self.kernel_manager.xreq_channel.execute('', silent=True)
290 msg_id = self.kernel_manager.xreq_channel.execute('', silent=True)
291 info = self._ExecutionRequest(msg_id, 'prompt')
291 info = self._ExecutionRequest(msg_id, 'prompt')
292 self._request_info['execute'] = info
292 self._request_info['execute'] = info
293 return
293 return
294
294
295 # Show a new prompt and save information about it so that it can be
295 # Show a new prompt and save information about it so that it can be
296 # updated later if the prompt number turns out to be wrong.
296 # updated later if the prompt number turns out to be wrong.
297 self._prompt_sep = self.input_sep
297 self._prompt_sep = self.input_sep
298 self._show_prompt(self._make_in_prompt(number), html=True)
298 self._show_prompt(self._make_in_prompt(number), html=True)
299 block = self._control.document().lastBlock()
299 block = self._control.document().lastBlock()
300 length = len(self._prompt)
300 length = len(self._prompt)
301 self._previous_prompt_obj = self._PromptBlock(block, length, number)
301 self._previous_prompt_obj = self._PromptBlock(block, length, number)
302
302
303 # Update continuation prompt to reflect (possibly) new prompt length.
303 # Update continuation prompt to reflect (possibly) new prompt length.
304 self._set_continuation_prompt(
304 self._set_continuation_prompt(
305 self._make_continuation_prompt(self._prompt), html=True)
305 self._make_continuation_prompt(self._prompt), html=True)
306
306
307 def _show_interpreter_prompt_for_reply(self, msg):
307 def _show_interpreter_prompt_for_reply(self, msg):
308 """ Reimplemented for IPython-style prompts.
308 """ Reimplemented for IPython-style prompts.
309 """
309 """
310 # Update the old prompt number if necessary.
310 # Update the old prompt number if necessary.
311 content = msg['content']
311 content = msg['content']
312 previous_prompt_number = content['execution_count']
312 previous_prompt_number = content['execution_count']
313 if self._previous_prompt_obj and \
313 if self._previous_prompt_obj and \
314 self._previous_prompt_obj.number != previous_prompt_number:
314 self._previous_prompt_obj.number != previous_prompt_number:
315 block = self._previous_prompt_obj.block
315 block = self._previous_prompt_obj.block
316
316
317 # Make sure the prompt block has not been erased.
317 # Make sure the prompt block has not been erased.
318 if block.isValid() and not block.text().isEmpty():
318 if block.isValid() and not block.text().isEmpty():
319
319
320 # Remove the old prompt and insert a new prompt.
320 # Remove the old prompt and insert a new prompt.
321 cursor = QtGui.QTextCursor(block)
321 cursor = QtGui.QTextCursor(block)
322 cursor.movePosition(QtGui.QTextCursor.Right,
322 cursor.movePosition(QtGui.QTextCursor.Right,
323 QtGui.QTextCursor.KeepAnchor,
323 QtGui.QTextCursor.KeepAnchor,
324 self._previous_prompt_obj.length)
324 self._previous_prompt_obj.length)
325 prompt = self._make_in_prompt(previous_prompt_number)
325 prompt = self._make_in_prompt(previous_prompt_number)
326 self._prompt = self._insert_html_fetching_plain_text(
326 self._prompt = self._insert_html_fetching_plain_text(
327 cursor, prompt)
327 cursor, prompt)
328
328
329 # When the HTML is inserted, Qt blows away the syntax
329 # When the HTML is inserted, Qt blows away the syntax
330 # highlighting for the line, so we need to rehighlight it.
330 # highlighting for the line, so we need to rehighlight it.
331 self._highlighter.rehighlightBlock(cursor.block())
331 self._highlighter.rehighlightBlock(cursor.block())
332
332
333 self._previous_prompt_obj = None
333 self._previous_prompt_obj = None
334
334
335 # Show a new prompt with the kernel's estimated prompt number.
335 # Show a new prompt with the kernel's estimated prompt number.
336 self._show_interpreter_prompt(previous_prompt_number+1)
336 self._show_interpreter_prompt(previous_prompt_number+1)
337
337
338 #---------------------------------------------------------------------------
338 #---------------------------------------------------------------------------
339 # 'IPythonWidget' interface
339 # 'IPythonWidget' interface
340 #---------------------------------------------------------------------------
340 #---------------------------------------------------------------------------
341
341
342 def set_default_style(self, lightbg=True):
342 def set_default_style(self, lightbg=True):
343 """ Sets the widget style to the class defaults.
343 """ Sets the widget style to the class defaults.
344
344
345 Parameters:
345 Parameters:
346 -----------
346 -----------
347 lightbg : bool, optional (default True)
347 lightbg : bool, optional (default True)
348 Whether to use the default IPython light background or dark
348 Whether to use the default IPython light background or dark
349 background style.
349 background style.
350 """
350 """
351 if lightbg:
351 if lightbg:
352 self.style_sheet = default_light_style_sheet
352 self.style_sheet = default_light_style_sheet
353 self.syntax_style = default_light_syntax_style
353 self.syntax_style = default_light_syntax_style
354 else:
354 else:
355 self.style_sheet = default_dark_style_sheet
355 self.style_sheet = default_dark_style_sheet
356 self.syntax_style = default_dark_syntax_style
356 self.syntax_style = default_dark_syntax_style
357
357
358 #---------------------------------------------------------------------------
358 #---------------------------------------------------------------------------
359 # 'IPythonWidget' protected interface
359 # 'IPythonWidget' protected interface
360 #---------------------------------------------------------------------------
360 #---------------------------------------------------------------------------
361
361
362 def _edit(self, filename, line=None):
362 def _edit(self, filename, line=None):
363 """ Opens a Python script for editing.
363 """ Opens a Python script for editing.
364
364
365 Parameters:
365 Parameters:
366 -----------
366 -----------
367 filename : str
367 filename : str
368 A path to a local system file.
368 A path to a local system file.
369
369
370 line : int, optional
370 line : int, optional
371 A line of interest in the file.
371 A line of interest in the file.
372 """
372 """
373 if self.custom_edit:
373 if self.custom_edit:
374 self.custom_edit_requested.emit(filename, line)
374 self.custom_edit_requested.emit(filename, line)
375 elif self.editor == 'default':
375 elif self.editor == 'default':
376 self._append_plain_text('No default editor available.\n')
376 self._append_plain_text('No default editor available.\n')
377 else:
377 else:
378 try:
378 try:
379 filename = '"%s"' % filename
379 filename = '"%s"' % filename
380 if line and self.editor_line:
380 if line and self.editor_line:
381 command = self.editor_line.format(filename=filename,
381 command = self.editor_line.format(filename=filename,
382 line=line)
382 line=line)
383 else:
383 else:
384 try:
384 try:
385 command = self.editor.format()
385 command = self.editor.format()
386 except KeyError:
386 except KeyError:
387 command = self.editor.format(filename=filename)
387 command = self.editor.format(filename=filename)
388 else:
388 else:
389 command += ' ' + filename
389 command += ' ' + filename
390 except KeyError:
390 except KeyError:
391 self._append_plain_text('Invalid editor command.\n')
391 self._append_plain_text('Invalid editor command.\n')
392 else:
392 else:
393 try:
393 try:
394 Popen(command, shell=True)
394 Popen(command, shell=True)
395 except OSError:
395 except OSError:
396 msg = 'Opening editor with command "%s" failed.\n'
396 msg = 'Opening editor with command "%s" failed.\n'
397 self._append_plain_text(msg % command)
397 self._append_plain_text(msg % command)
398
398
399 def _make_in_prompt(self, number):
399 def _make_in_prompt(self, number):
400 """ Given a prompt number, returns an HTML In prompt.
400 """ Given a prompt number, returns an HTML In prompt.
401 """
401 """
402 body = self.in_prompt % number
402 body = self.in_prompt % number
403 return '<span class="in-prompt">%s</span>' % body
403 return '<span class="in-prompt">%s</span>' % body
404
404
405 def _make_continuation_prompt(self, prompt):
405 def _make_continuation_prompt(self, prompt):
406 """ Given a plain text version of an In prompt, returns an HTML
406 """ Given a plain text version of an In prompt, returns an HTML
407 continuation prompt.
407 continuation prompt.
408 """
408 """
409 end_chars = '...: '
409 end_chars = '...: '
410 space_count = len(prompt.lstrip('\n')) - len(end_chars)
410 space_count = len(prompt.lstrip('\n')) - len(end_chars)
411 body = '&nbsp;' * space_count + end_chars
411 body = '&nbsp;' * space_count + end_chars
412 return '<span class="in-prompt">%s</span>' % body
412 return '<span class="in-prompt">%s</span>' % body
413
413
414 def _make_out_prompt(self, number):
414 def _make_out_prompt(self, number):
415 """ Given a prompt number, returns an HTML Out prompt.
415 """ Given a prompt number, returns an HTML Out prompt.
416 """
416 """
417 body = self.out_prompt % number
417 body = self.out_prompt % number
418 return '<span class="out-prompt">%s</span>' % body
418 return '<span class="out-prompt">%s</span>' % body
419
419
420 #------ Payload handlers --------------------------------------------------
420 #------ Payload handlers --------------------------------------------------
421
421
422 # Payload handlers with a generic interface: each takes the opaque payload
422 # Payload handlers with a generic interface: each takes the opaque payload
423 # dict, unpacks it and calls the underlying functions with the necessary
423 # dict, unpacks it and calls the underlying functions with the necessary
424 # arguments.
424 # arguments.
425
425
426 def _handle_payload_edit(self, item):
426 def _handle_payload_edit(self, item):
427 self._edit(item['filename'], item['line_number'])
427 self._edit(item['filename'], item['line_number'])
428
428
429 def _handle_payload_exit(self, item):
429 def _handle_payload_exit(self, item):
430 self.exit_requested.emit()
430 self.exit_requested.emit()
431
431
432 def _handle_payload_page(self, item):
432 def _handle_payload_page(self, item):
433 self._page(item['data'])
433 self._page(item['text'])
434
434
435 #------ Trait change handlers ---------------------------------------------
435 #------ Trait change handlers ---------------------------------------------
436
436
437 def _style_sheet_changed(self):
437 def _style_sheet_changed(self):
438 """ Set the style sheets of the underlying widgets.
438 """ Set the style sheets of the underlying widgets.
439 """
439 """
440 self.setStyleSheet(self.style_sheet)
440 self.setStyleSheet(self.style_sheet)
441 self._control.document().setDefaultStyleSheet(self.style_sheet)
441 self._control.document().setDefaultStyleSheet(self.style_sheet)
442 if self._page_control:
442 if self._page_control:
443 self._page_control.document().setDefaultStyleSheet(self.style_sheet)
443 self._page_control.document().setDefaultStyleSheet(self.style_sheet)
444
444
445 bg_color = self._control.palette().background().color()
445 bg_color = self._control.palette().background().color()
446 self._ansi_processor.set_background_color(bg_color)
446 self._ansi_processor.set_background_color(bg_color)
447
447
448 def _syntax_style_changed(self):
448 def _syntax_style_changed(self):
449 """ Set the style for the syntax highlighter.
449 """ Set the style for the syntax highlighter.
450 """
450 """
451 if self.syntax_style:
451 if self.syntax_style:
452 self._highlighter.set_style(self.syntax_style)
452 self._highlighter.set_style(self.syntax_style)
453 else:
453 else:
454 self._highlighter.set_style_sheet(self.style_sheet)
454 self._highlighter.set_style_sheet(self.style_sheet)
455
455
@@ -1,545 +1,545 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' % arg_s, split=False))
533 page.page(self.shell.getoutput('man %s' % arg_s, split=False))
534
534
535 # FIXME: this is specific to the GUI, so we should let the gui app load
535 # FIXME: this is specific to the GUI, so we should let the gui app load
536 # magics at startup that are only for the gui. Once the gui app has proper
536 # magics at startup that are only for the gui. Once the gui app has proper
537 # profile and configuration management, we can have it initialize a kernel
537 # profile and configuration management, we can have it initialize a kernel
538 # with a special config file that provides these.
538 # with a special config file that provides these.
539 def magic_guiref(self, arg_s):
539 def magic_guiref(self, arg_s):
540 """Show a basic reference about the GUI console."""
540 """Show a basic reference about the GUI console."""
541 from IPython.core.usage import gui_reference
541 from IPython.core.usage import gui_reference
542 page.page(gui_reference)
542 page.page(gui_reference, auto_html=True)
543
543
544
544
545 InteractiveShellABC.register(ZMQInteractiveShell)
545 InteractiveShellABC.register(ZMQInteractiveShell)
General Comments 0
You need to be logged in to leave comments. Login now