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