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