##// END OF EJS Templates
enable %gui/%pylab magics in the Kernel...
MinRK -
Show More
@@ -1,322 +1,322 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Pylab (matplotlib) support utilities.
3 3
4 4 Authors
5 5 -------
6 6
7 7 * Fernando Perez.
8 8 * Brian Granger
9 9 """
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Copyright (C) 2009 The IPython Development Team
13 13 #
14 14 # Distributed under the terms of the BSD License. The full license is in
15 15 # the file COPYING, distributed as part of this software.
16 16 #-----------------------------------------------------------------------------
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Imports
20 20 #-----------------------------------------------------------------------------
21 21
22 22 from io import BytesIO
23 23
24 24 from IPython.utils.decorators import flag_calls
25 25
26 26 # If user specifies a GUI, that dictates the backend, otherwise we read the
27 27 # user's mpl default from the mpl rc structure
28 28 backends = {'tk': 'TkAgg',
29 29 'gtk': 'GTKAgg',
30 30 'wx': 'WXAgg',
31 31 'qt': 'Qt4Agg', # qt3 not supported
32 32 'qt4': 'Qt4Agg',
33 33 'osx': 'MacOSX',
34 34 'inline' : 'module://IPython.zmq.pylab.backend_inline'}
35 35
36 36 # We also need a reverse backends2guis mapping that will properly choose which
37 37 # GUI support to activate based on the desired matplotlib backend. For the
38 38 # most part it's just a reverse of the above dict, but we also need to add a
39 39 # few others that map to the same GUI manually:
40 40 backend2gui = dict(zip(backends.values(), backends.keys()))
41 41 # In the reverse mapping, there are a few extra valid matplotlib backends that
42 42 # map to the same GUI support
43 43 backend2gui['GTK'] = backend2gui['GTKCairo'] = 'gtk'
44 44 backend2gui['WX'] = 'wx'
45 45 backend2gui['CocoaAgg'] = 'osx'
46 46
47 47 #-----------------------------------------------------------------------------
48 48 # Matplotlib utilities
49 49 #-----------------------------------------------------------------------------
50 50
51 51
52 52 def getfigs(*fig_nums):
53 53 """Get a list of matplotlib figures by figure numbers.
54 54
55 55 If no arguments are given, all available figures are returned. If the
56 56 argument list contains references to invalid figures, a warning is printed
57 57 but the function continues pasting further figures.
58 58
59 59 Parameters
60 60 ----------
61 61 figs : tuple
62 62 A tuple of ints giving the figure numbers of the figures to return.
63 63 """
64 64 from matplotlib._pylab_helpers import Gcf
65 65 if not fig_nums:
66 66 fig_managers = Gcf.get_all_fig_managers()
67 67 return [fm.canvas.figure for fm in fig_managers]
68 68 else:
69 69 figs = []
70 70 for num in fig_nums:
71 71 f = Gcf.figs.get(num)
72 72 if f is None:
73 73 print('Warning: figure %s not available.' % num)
74 74 else:
75 75 figs.append(f.canvas.figure)
76 76 return figs
77 77
78 78
79 79 def figsize(sizex, sizey):
80 80 """Set the default figure size to be [sizex, sizey].
81 81
82 82 This is just an easy to remember, convenience wrapper that sets::
83 83
84 84 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
85 85 """
86 86 import matplotlib
87 87 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
88 88
89 89
90 90 def print_figure(fig, fmt='png'):
91 91 """Convert a figure to svg or png for inline display."""
92 92 # When there's an empty figure, we shouldn't return anything, otherwise we
93 93 # get big blank areas in the qt console.
94 94 if not fig.axes:
95 95 return
96 96
97 97 fc = fig.get_facecolor()
98 98 ec = fig.get_edgecolor()
99 99 fig.set_facecolor('white')
100 100 fig.set_edgecolor('white')
101 101 try:
102 102 bytes_io = BytesIO()
103 103 # use 72 dpi to match QTConsole's dpi
104 104 fig.canvas.print_figure(bytes_io, format=fmt, dpi=72,
105 105 bbox_inches='tight')
106 106 data = bytes_io.getvalue()
107 107 finally:
108 108 fig.set_facecolor(fc)
109 109 fig.set_edgecolor(ec)
110 110 return data
111 111
112 112
113 113 # We need a little factory function here to create the closure where
114 114 # safe_execfile can live.
115 115 def mpl_runner(safe_execfile):
116 116 """Factory to return a matplotlib-enabled runner for %run.
117 117
118 118 Parameters
119 119 ----------
120 120 safe_execfile : function
121 121 This must be a function with the same interface as the
122 122 :meth:`safe_execfile` method of IPython.
123 123
124 124 Returns
125 125 -------
126 126 A function suitable for use as the ``runner`` argument of the %run magic
127 127 function.
128 128 """
129 129
130 130 def mpl_execfile(fname,*where,**kw):
131 131 """matplotlib-aware wrapper around safe_execfile.
132 132
133 133 Its interface is identical to that of the :func:`execfile` builtin.
134 134
135 135 This is ultimately a call to execfile(), but wrapped in safeties to
136 136 properly handle interactive rendering."""
137 137
138 138 import matplotlib
139 139 import matplotlib.pylab as pylab
140 140
141 141 #print '*** Matplotlib runner ***' # dbg
142 142 # turn off rendering until end of script
143 143 is_interactive = matplotlib.rcParams['interactive']
144 144 matplotlib.interactive(False)
145 145 safe_execfile(fname,*where,**kw)
146 146 matplotlib.interactive(is_interactive)
147 147 # make rendering call now, if the user tried to do it
148 148 if pylab.draw_if_interactive.called:
149 149 pylab.draw()
150 150 pylab.draw_if_interactive.called = False
151 151
152 152 return mpl_execfile
153 153
154 154
155 155 def select_figure_format(shell, fmt):
156 156 """Select figure format for inline backend, either 'png' or 'svg'.
157 157
158 158 Using this method ensures only one figure format is active at a time.
159 159 """
160 160 from matplotlib.figure import Figure
161 161 from IPython.zmq.pylab import backend_inline
162 162
163 163 svg_formatter = shell.display_formatter.formatters['image/svg+xml']
164 164 png_formatter = shell.display_formatter.formatters['image/png']
165 165
166 166 if fmt=='png':
167 167 svg_formatter.type_printers.pop(Figure, None)
168 168 png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png'))
169 169 elif fmt=='svg':
170 170 png_formatter.type_printers.pop(Figure, None)
171 171 svg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'svg'))
172 172 else:
173 173 raise ValueError("supported formats are: 'png', 'svg', not %r"%fmt)
174 174
175 175 # set the format to be used in the backend()
176 176 backend_inline._figure_format = fmt
177 177
178 178 #-----------------------------------------------------------------------------
179 179 # Code for initializing matplotlib and importing pylab
180 180 #-----------------------------------------------------------------------------
181 181
182 182
183 183 def find_gui_and_backend(gui=None):
184 184 """Given a gui string return the gui and mpl backend.
185 185
186 186 Parameters
187 187 ----------
188 188 gui : str
189 189 Can be one of ('tk','gtk','wx','qt','qt4','inline').
190 190
191 191 Returns
192 192 -------
193 193 A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
194 194 'WXAgg','Qt4Agg','module://IPython.zmq.pylab.backend_inline').
195 195 """
196 196
197 197 import matplotlib
198 198
199 199 if gui:
200 200 # select backend based on requested gui
201 201 backend = backends[gui]
202 202 else:
203 203 backend = matplotlib.rcParams['backend']
204 204 # In this case, we need to find what the appropriate gui selection call
205 205 # should be for IPython, so we can activate inputhook accordingly
206 206 gui = backend2gui.get(backend, None)
207 207 return gui, backend
208 208
209 209
210 210 def activate_matplotlib(backend):
211 211 """Activate the given backend and set interactive to True."""
212 212
213 213 import matplotlib
214 214 if backend.startswith('module://'):
215 215 # Work around bug in matplotlib: matplotlib.use converts the
216 216 # backend_id to lowercase even if a module name is specified!
217 217 matplotlib.rcParams['backend'] = backend
218 218 else:
219 219 matplotlib.use(backend)
220 220 matplotlib.interactive(True)
221 221
222 222 # This must be imported last in the matplotlib series, after
223 223 # backend/interactivity choices have been made
224 224 import matplotlib.pylab as pylab
225 225
226 226 # XXX For now leave this commented out, but depending on discussions with
227 227 # mpl-dev, we may be able to allow interactive switching...
228 228 #import matplotlib.pyplot
229 229 #matplotlib.pyplot.switch_backend(backend)
230 230
231 231 pylab.show._needmain = False
232 232 # We need to detect at runtime whether show() is called by the user.
233 233 # For this, we wrap it into a decorator which adds a 'called' flag.
234 234 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
235 235
236 236 def import_pylab(user_ns, backend, import_all=True, shell=None):
237 237 """Import the standard pylab symbols into user_ns."""
238 238
239 239 # Import numpy as np/pyplot as plt are conventions we're trying to
240 240 # somewhat standardize on. Making them available to users by default
241 241 # will greatly help this.
242 242 s = ("import numpy\n"
243 243 "import matplotlib\n"
244 244 "from matplotlib import pylab, mlab, pyplot\n"
245 245 "np = numpy\n"
246 246 "plt = pyplot\n"
247 247 )
248 248 exec s in user_ns
249 249
250 250 if shell is not None:
251 251 exec s in shell.user_ns_hidden
252 252 # If using our svg payload backend, register the post-execution
253 253 # function that will pick up the results for display. This can only be
254 254 # done with access to the real shell object.
255 255 #
256 256 from IPython.zmq.pylab.backend_inline import InlineBackendConfig
257 257
258 258 cfg = InlineBackendConfig.instance(config=shell.config)
259 259 cfg.shell = shell
260 260
261 261 if backend == backends['inline']:
262 262 from IPython.zmq.pylab.backend_inline import flush_figures
263 263 from matplotlib import pyplot
264 264 shell.register_post_execute(flush_figures)
265 265 # load inline_rc
266 266 pyplot.rcParams.update(cfg.rc)
267 267
268 268 # Add 'figsize' to pyplot and to the user's namespace
269 269 user_ns['figsize'] = pyplot.figsize = figsize
270 270 shell.user_ns_hidden['figsize'] = figsize
271 271
272 272 # Setup the default figure format
273 273 fmt = cfg.figure_format
274 274 select_figure_format(shell, fmt)
275 275
276 276 # The old pastefig function has been replaced by display
277 277 from IPython.core.display import display
278 278 # Add display and display_png to the user's namespace
279 279 user_ns['display'] = display
280 280 shell.user_ns_hidden['display'] = display
281 281 user_ns['getfigs'] = getfigs
282 282 shell.user_ns_hidden['getfigs'] = getfigs
283 283
284 284 if import_all:
285 285 s = ("from matplotlib.pylab import *\n"
286 286 "from numpy import *\n")
287 287 exec s in user_ns
288 288 if shell is not None:
289 289 exec s in shell.user_ns_hidden
290 290
291 291
292 def pylab_activate(user_ns, gui=None, import_all=True):
292 def pylab_activate(user_ns, gui=None, import_all=True, shell=None):
293 293 """Activate pylab mode in the user's namespace.
294 294
295 295 Loads and initializes numpy, matplotlib and friends for interactive use.
296 296
297 297 Parameters
298 298 ----------
299 299 user_ns : dict
300 300 Namespace where the imports will occur.
301 301
302 302 gui : optional, string
303 303 A valid gui name following the conventions of the %gui magic.
304 304
305 305 import_all : optional, boolean
306 306 If true, an 'import *' is done from numpy and pylab.
307 307
308 308 Returns
309 309 -------
310 310 The actual gui used (if not given as input, it was obtained from matplotlib
311 311 itself, and will be needed next to configure IPython's gui integration.
312 312 """
313 313 gui, backend = find_gui_and_backend(gui)
314 314 activate_matplotlib(backend)
315 import_pylab(user_ns, backend, import_all)
315 import_pylab(user_ns, backend, import_all, shell)
316 316
317 317 print """
318 318 Welcome to pylab, a matplotlib-based Python environment [backend: %s].
319 319 For more information, type 'help(pylab)'.""" % backend
320 320
321 321 return gui
322 322
@@ -1,782 +1,790 b''
1 1 #!/usr/bin/env python
2 2 """A simple interactive kernel that talks to a frontend over 0MQ.
3 3
4 4 Things to do:
5 5
6 6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
7 7 call set_parent on all the PUB objects with the message about to be executed.
8 8 * Implement random port and security key logic.
9 9 * Implement control messages.
10 10 * Implement event loop and poll version.
11 11 """
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from __future__ import print_function
17 17
18 18 # Standard library imports.
19 19 import __builtin__
20 20 import atexit
21 21 import sys
22 22 import time
23 23 import traceback
24 24 import logging
25
25 26 # System library imports.
26 27 import zmq
27 28
28 29 # Local imports.
29 30 from IPython.config.configurable import Configurable
30 31 from IPython.config.application import boolean_flag
31 32 from IPython.core.application import ProfileDir
32 33 from IPython.core.error import StdinNotImplementedError
33 34 from IPython.core.shellapp import (
34 35 InteractiveShellApp, shell_flags, shell_aliases
35 36 )
36 37 from IPython.utils import io
37 38 from IPython.utils import py3compat
38 39 from IPython.utils.jsonutil import json_clean
39 40 from IPython.lib import pylabtools
40 41 from IPython.utils.traitlets import (
41 List, Instance, Float, Dict, Bool, Int, Unicode, CaselessStrEnum
42 Any, List, Instance, Float, Dict, Bool, Int, Unicode, CaselessStrEnum
42 43 )
43 44
44 45 from entry_point import base_launch_kernel
45 46 from kernelapp import KernelApp, kernel_flags, kernel_aliases
46 47 from iostream import OutStream
47 48 from session import Session, Message
48 49 from zmqshell import ZMQInteractiveShell
49 50
50 51
51 52 #-----------------------------------------------------------------------------
52 53 # Main kernel class
53 54 #-----------------------------------------------------------------------------
54 55
55 56 class Kernel(Configurable):
56 57
57 58 #---------------------------------------------------------------------------
58 59 # Kernel interface
59 60 #---------------------------------------------------------------------------
60 61
62 # attribute to override with a GUI
63 eventloop = Any(None)
64
61 65 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
62 66 session = Instance(Session)
63 67 shell_socket = Instance('zmq.Socket')
64 68 iopub_socket = Instance('zmq.Socket')
65 69 stdin_socket = Instance('zmq.Socket')
66 70 log = Instance(logging.Logger)
67 71
68 72 # Private interface
69 73
70 74 # Time to sleep after flushing the stdout/err buffers in each execute
71 75 # cycle. While this introduces a hard limit on the minimal latency of the
72 76 # execute cycle, it helps prevent output synchronization problems for
73 77 # clients.
74 78 # Units are in seconds. The minimum zmq latency on local host is probably
75 79 # ~150 microseconds, set this to 500us for now. We may need to increase it
76 80 # a little if it's not enough after more interactive testing.
77 81 _execute_sleep = Float(0.0005, config=True)
78 82
79 83 # Frequency of the kernel's event loop.
80 84 # Units are in seconds, kernel subclasses for GUI toolkits may need to
81 85 # adapt to milliseconds.
82 86 _poll_interval = Float(0.05, config=True)
83 87
84 88 # If the shutdown was requested over the network, we leave here the
85 89 # necessary reply message so it can be sent by our registered atexit
86 90 # handler. This ensures that the reply is only sent to clients truly at
87 91 # the end of our shutdown process (which happens after the underlying
88 92 # IPython shell's own shutdown).
89 93 _shutdown_message = None
90 94
91 95 # This is a dict of port number that the kernel is listening on. It is set
92 96 # by record_ports and used by connect_request.
93 97 _recorded_ports = Dict()
94 98
95 99
96 100
97 101 def __init__(self, **kwargs):
98 102 super(Kernel, self).__init__(**kwargs)
99 103
100 104 # Before we even start up the shell, register *first* our exit handlers
101 105 # so they come before the shell's
102 106 atexit.register(self._at_shutdown)
103 107
104 108 # Initialize the InteractiveShell subclass
105 109 self.shell = ZMQInteractiveShell.instance(config=self.config)
106 110 self.shell.displayhook.session = self.session
107 111 self.shell.displayhook.pub_socket = self.iopub_socket
108 112 self.shell.display_pub.session = self.session
109 113 self.shell.display_pub.pub_socket = self.iopub_socket
110 114
111 115 # TMP - hack while developing
112 116 self.shell._reply_content = None
113 117
114 118 # Build dict of handlers for message types
115 119 msg_types = [ 'execute_request', 'complete_request',
116 120 'object_info_request', 'history_request',
117 121 'connect_request', 'shutdown_request']
118 122 self.handlers = {}
119 123 for msg_type in msg_types:
120 124 self.handlers[msg_type] = getattr(self, msg_type)
121 125
122 126 def do_one_iteration(self):
123 127 """Do one iteration of the kernel's evaluation loop.
124 128 """
125 129 try:
126 130 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
127 131 except Exception:
128 132 self.log.warn("Invalid Message:", exc_info=True)
129 133 return
130 134 if msg is None:
131 135 return
132 136
133 137 msg_type = msg['header']['msg_type']
134 138
135 139 # This assert will raise in versions of zeromq 2.0.7 and lesser.
136 140 # We now require 2.0.8 or above, so we can uncomment for safety.
137 141 # print(ident,msg, file=sys.__stdout__)
138 142 assert ident is not None, "Missing message part."
139 143
140 144 # Print some info about this message and leave a '--->' marker, so it's
141 145 # easier to trace visually the message chain when debugging. Each
142 146 # handler prints its message at the end.
143 147 self.log.debug('\n*** MESSAGE TYPE:'+str(msg_type)+'***')
144 148 self.log.debug(' Content: '+str(msg['content'])+'\n --->\n ')
145 149
146 150 # Find and call actual handler for message
147 151 handler = self.handlers.get(msg_type, None)
148 152 if handler is None:
149 153 self.log.error("UNKNOWN MESSAGE TYPE:" +str(msg))
150 154 else:
151 155 handler(ident, msg)
152 156
153 157 # Check whether we should exit, in case the incoming message set the
154 158 # exit flag on
155 159 if self.shell.exit_now:
156 160 self.log.debug('\nExiting IPython kernel...')
157 161 # We do a normal, clean exit, which allows any actions registered
158 162 # via atexit (such as history saving) to take place.
159 163 sys.exit(0)
160 164
161 165
162 166 def start(self):
163 167 """ Start the kernel main loop.
164 168 """
165 169 poller = zmq.Poller()
166 170 poller.register(self.shell_socket, zmq.POLLIN)
167 while True:
171 # loop while self.eventloop has not been overridden
172 while self.eventloop is None:
168 173 try:
169 174 # scale by extra factor of 10, because there is no
170 175 # reason for this to be anything less than ~ 0.1s
171 176 # since it is a real poller and will respond
172 177 # to events immediately
173 178
174 179 # double nested try/except, to properly catch KeyboardInterrupt
175 180 # due to pyzmq Issue #130
176 181 try:
177 182 poller.poll(10*1000*self._poll_interval)
178 183 self.do_one_iteration()
179 184 except:
180 185 raise
181 186 except KeyboardInterrupt:
182 187 # Ctrl-C shouldn't crash the kernel
183 188 io.raw_print("KeyboardInterrupt caught in kernel")
189 if self.eventloop is not None:
190 try:
191 self.eventloop(self)
192 except KeyboardInterrupt:
193 # Ctrl-C shouldn't crash the kernel
194 io.raw_print("KeyboardInterrupt caught in kernel")
195
184 196
185 197 def record_ports(self, ports):
186 198 """Record the ports that this kernel is using.
187 199
188 200 The creator of the Kernel instance must call this methods if they
189 201 want the :meth:`connect_request` method to return the port numbers.
190 202 """
191 203 self._recorded_ports = ports
192 204
193 205 #---------------------------------------------------------------------------
194 206 # Kernel request handlers
195 207 #---------------------------------------------------------------------------
196 208
197 209 def _publish_pyin(self, code, parent):
198 210 """Publish the code request on the pyin stream."""
199 211
200 212 pyin_msg = self.session.send(self.iopub_socket, u'pyin',{u'code':code}, parent=parent)
201 213
202 214 def execute_request(self, ident, parent):
203 215
204 216 status_msg = self.session.send(self.iopub_socket,
205 217 u'status',
206 218 {u'execution_state':u'busy'},
207 219 parent=parent
208 220 )
209 221
210 222 try:
211 223 content = parent[u'content']
212 224 code = content[u'code']
213 225 silent = content[u'silent']
214 226 except:
215 227 self.log.error("Got bad msg: ")
216 228 self.log.error(str(Message(parent)))
217 229 return
218 230
219 231 shell = self.shell # we'll need this a lot here
220 232
221 233 # Replace raw_input. Note that is not sufficient to replace
222 234 # raw_input in the user namespace.
223 235 if content.get('allow_stdin', False):
224 236 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
225 237 else:
226 238 raw_input = lambda prompt='' : self._no_raw_input()
227 239
228 240 if py3compat.PY3:
229 241 __builtin__.input = raw_input
230 242 else:
231 243 __builtin__.raw_input = raw_input
232 244
233 245 # Set the parent message of the display hook and out streams.
234 246 shell.displayhook.set_parent(parent)
235 247 shell.display_pub.set_parent(parent)
236 248 sys.stdout.set_parent(parent)
237 249 sys.stderr.set_parent(parent)
238 250
239 251 # Re-broadcast our input for the benefit of listening clients, and
240 252 # start computing output
241 253 if not silent:
242 254 self._publish_pyin(code, parent)
243 255
244 256 reply_content = {}
245 257 try:
246 258 if silent:
247 259 # run_code uses 'exec' mode, so no displayhook will fire, and it
248 260 # doesn't call logging or history manipulations. Print
249 261 # statements in that code will obviously still execute.
250 262 shell.run_code(code)
251 263 else:
252 264 # FIXME: the shell calls the exception handler itself.
253 265 shell.run_cell(code, store_history=True)
254 266 except:
255 267 status = u'error'
256 268 # FIXME: this code right now isn't being used yet by default,
257 269 # because the run_cell() call above directly fires off exception
258 270 # reporting. This code, therefore, is only active in the scenario
259 271 # where runlines itself has an unhandled exception. We need to
260 272 # uniformize this, for all exception construction to come from a
261 273 # single location in the codbase.
262 274 etype, evalue, tb = sys.exc_info()
263 275 tb_list = traceback.format_exception(etype, evalue, tb)
264 276 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
265 277 else:
266 278 status = u'ok'
267 279
268 280 reply_content[u'status'] = status
269 281
270 282 # Return the execution counter so clients can display prompts
271 283 reply_content['execution_count'] = shell.execution_count -1
272 284
273 285 # FIXME - fish exception info out of shell, possibly left there by
274 286 # runlines. We'll need to clean up this logic later.
275 287 if shell._reply_content is not None:
276 288 reply_content.update(shell._reply_content)
277 289 # reset after use
278 290 shell._reply_content = None
279 291
280 292 # At this point, we can tell whether the main code execution succeeded
281 293 # or not. If it did, we proceed to evaluate user_variables/expressions
282 294 if reply_content['status'] == 'ok':
283 295 reply_content[u'user_variables'] = \
284 296 shell.user_variables(content[u'user_variables'])
285 297 reply_content[u'user_expressions'] = \
286 298 shell.user_expressions(content[u'user_expressions'])
287 299 else:
288 300 # If there was an error, don't even try to compute variables or
289 301 # expressions
290 302 reply_content[u'user_variables'] = {}
291 303 reply_content[u'user_expressions'] = {}
292 304
293 305 # Payloads should be retrieved regardless of outcome, so we can both
294 306 # recover partial output (that could have been generated early in a
295 307 # block, before an error) and clear the payload system always.
296 308 reply_content[u'payload'] = shell.payload_manager.read_payload()
297 309 # Be agressive about clearing the payload because we don't want
298 310 # it to sit in memory until the next execute_request comes in.
299 311 shell.payload_manager.clear_payload()
300 312
301 313 # Flush output before sending the reply.
302 314 sys.stdout.flush()
303 315 sys.stderr.flush()
304 316 # FIXME: on rare occasions, the flush doesn't seem to make it to the
305 317 # clients... This seems to mitigate the problem, but we definitely need
306 318 # to better understand what's going on.
307 319 if self._execute_sleep:
308 320 time.sleep(self._execute_sleep)
309 321
310 322 # Send the reply.
311 323 reply_content = json_clean(reply_content)
312 324 reply_msg = self.session.send(self.shell_socket, u'execute_reply',
313 325 reply_content, parent, ident=ident)
314 326 self.log.debug(str(reply_msg))
315 327
316 328 if reply_msg['content']['status'] == u'error':
317 329 self._abort_queue()
318 330
319 331 status_msg = self.session.send(self.iopub_socket,
320 332 u'status',
321 333 {u'execution_state':u'idle'},
322 334 parent=parent
323 335 )
324 336
325 337 def complete_request(self, ident, parent):
326 338 txt, matches = self._complete(parent)
327 339 matches = {'matches' : matches,
328 340 'matched_text' : txt,
329 341 'status' : 'ok'}
330 342 matches = json_clean(matches)
331 343 completion_msg = self.session.send(self.shell_socket, 'complete_reply',
332 344 matches, parent, ident)
333 345 self.log.debug(str(completion_msg))
334 346
335 347 def object_info_request(self, ident, parent):
336 348 object_info = self.shell.object_inspect(parent['content']['oname'])
337 349 # Before we send this object over, we scrub it for JSON usage
338 350 oinfo = json_clean(object_info)
339 351 msg = self.session.send(self.shell_socket, 'object_info_reply',
340 352 oinfo, parent, ident)
341 353 self.log.debug(msg)
342 354
343 355 def history_request(self, ident, parent):
344 356 # We need to pull these out, as passing **kwargs doesn't work with
345 357 # unicode keys before Python 2.6.5.
346 358 hist_access_type = parent['content']['hist_access_type']
347 359 raw = parent['content']['raw']
348 360 output = parent['content']['output']
349 361 if hist_access_type == 'tail':
350 362 n = parent['content']['n']
351 363 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
352 364 include_latest=True)
353 365
354 366 elif hist_access_type == 'range':
355 367 session = parent['content']['session']
356 368 start = parent['content']['start']
357 369 stop = parent['content']['stop']
358 370 hist = self.shell.history_manager.get_range(session, start, stop,
359 371 raw=raw, output=output)
360 372
361 373 elif hist_access_type == 'search':
362 374 pattern = parent['content']['pattern']
363 375 hist = self.shell.history_manager.search(pattern, raw=raw, output=output)
364 376
365 377 else:
366 378 hist = []
367 379 content = {'history' : list(hist)}
368 380 content = json_clean(content)
369 381 msg = self.session.send(self.shell_socket, 'history_reply',
370 382 content, parent, ident)
371 383 self.log.debug(str(msg))
372 384
373 385 def connect_request(self, ident, parent):
374 386 if self._recorded_ports is not None:
375 387 content = self._recorded_ports.copy()
376 388 else:
377 389 content = {}
378 390 msg = self.session.send(self.shell_socket, 'connect_reply',
379 391 content, parent, ident)
380 392 self.log.debug(msg)
381 393
382 394 def shutdown_request(self, ident, parent):
383 395 self.shell.exit_now = True
384 396 self._shutdown_message = self.session.msg(u'shutdown_reply', parent['content'], parent)
385 397 sys.exit(0)
386 398
387 399 #---------------------------------------------------------------------------
388 400 # Protected interface
389 401 #---------------------------------------------------------------------------
390 402
391 403 def _abort_queue(self):
392 404 while True:
393 405 try:
394 406 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
395 407 except Exception:
396 408 self.log.warn("Invalid Message:", exc_info=True)
397 409 continue
398 410 if msg is None:
399 411 break
400 412 else:
401 413 assert ident is not None, \
402 414 "Unexpected missing message part."
403 415
404 416 self.log.debug("Aborting:\n"+str(Message(msg)))
405 417 msg_type = msg['header']['msg_type']
406 418 reply_type = msg_type.split('_')[0] + '_reply'
407 419 reply_msg = self.session.send(self.shell_socket, reply_type,
408 420 {'status' : 'aborted'}, msg, ident=ident)
409 421 self.log.debug(reply_msg)
410 422 # We need to wait a bit for requests to come in. This can probably
411 423 # be set shorter for true asynchronous clients.
412 424 time.sleep(0.1)
413 425
414 426 def _no_raw_input(self):
415 427 """Raise StdinNotImplentedError if active frontend doesn't support stdin."""
416 428 raise StdinNotImplementedError("raw_input was called, but this frontend does not support stdin.")
417 429
418 430 def _raw_input(self, prompt, ident, parent):
419 431 # Flush output before making the request.
420 432 sys.stderr.flush()
421 433 sys.stdout.flush()
422 434
423 435 # Send the input request.
424 436 content = json_clean(dict(prompt=prompt))
425 437 msg = self.session.send(self.stdin_socket, u'input_request', content, parent, ident=ident)
426 438
427 439 # Await a response.
428 440 while True:
429 441 try:
430 442 ident, reply = self.session.recv(self.stdin_socket, 0)
431 443 except Exception:
432 444 self.log.warn("Invalid Message:", exc_info=True)
433 445 else:
434 446 break
435 447 try:
436 448 value = reply['content']['value']
437 449 except:
438 450 self.log.error("Got bad raw_input reply: ")
439 451 self.log.error(str(Message(parent)))
440 452 value = ''
441 453 return value
442 454
443 455 def _complete(self, msg):
444 456 c = msg['content']
445 457 try:
446 458 cpos = int(c['cursor_pos'])
447 459 except:
448 460 # If we don't get something that we can convert to an integer, at
449 461 # least attempt the completion guessing the cursor is at the end of
450 462 # the text, if there's any, and otherwise of the line
451 463 cpos = len(c['text'])
452 464 if cpos==0:
453 465 cpos = len(c['line'])
454 466 return self.shell.complete(c['text'], c['line'], cpos)
455 467
456 468 def _object_info(self, context):
457 469 symbol, leftover = self._symbol_from_context(context)
458 470 if symbol is not None and not leftover:
459 471 doc = getattr(symbol, '__doc__', '')
460 472 else:
461 473 doc = ''
462 474 object_info = dict(docstring = doc)
463 475 return object_info
464 476
465 477 def _symbol_from_context(self, context):
466 478 if not context:
467 479 return None, context
468 480
469 481 base_symbol_string = context[0]
470 482 symbol = self.shell.user_ns.get(base_symbol_string, None)
471 483 if symbol is None:
472 484 symbol = __builtin__.__dict__.get(base_symbol_string, None)
473 485 if symbol is None:
474 486 return None, context
475 487
476 488 context = context[1:]
477 489 for i, name in enumerate(context):
478 490 new_symbol = getattr(symbol, name, None)
479 491 if new_symbol is None:
480 492 return symbol, context[i:]
481 493 else:
482 494 symbol = new_symbol
483 495
484 496 return symbol, []
485 497
486 498 def _at_shutdown(self):
487 499 """Actions taken at shutdown by the kernel, called by python's atexit.
488 500 """
489 501 # io.rprint("Kernel at_shutdown") # dbg
490 502 if self._shutdown_message is not None:
491 503 self.session.send(self.shell_socket, self._shutdown_message)
492 504 self.session.send(self.iopub_socket, self._shutdown_message)
493 505 self.log.debug(str(self._shutdown_message))
494 506 # A very short sleep to give zmq time to flush its message buffers
495 507 # before Python truly shuts down.
496 508 time.sleep(0.01)
497 509
498 510
499 class QtKernel(Kernel):
500 """A Kernel subclass with Qt support."""
511 #------------------------------------------------------------------------------
512 # Eventloops for integrating the Kernel into different GUIs
513 #------------------------------------------------------------------------------
501 514
502 def start(self):
503 """Start a kernel with QtPy4 event loop integration."""
504 515
505 from IPython.external.qt_for_kernel import QtCore
506 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
516 def loop_qt4(kernel):
517 """Start a kernel with PyQt4 event loop integration."""
507 518
508 self.app = get_app_qt4([" "])
509 self.app.setQuitOnLastWindowClosed(False)
510 self.timer = QtCore.QTimer()
511 self.timer.timeout.connect(self.do_one_iteration)
512 # Units for the timer are in milliseconds
513 self.timer.start(1000*self._poll_interval)
514 start_event_loop_qt4(self.app)
519 from IPython.external.qt_for_kernel import QtCore
520 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
515 521
522 kernel.app = get_app_qt4([" "])
523 kernel.app.setQuitOnLastWindowClosed(False)
524 kernel.timer = QtCore.QTimer()
525 kernel.timer.timeout.connect(kernel.do_one_iteration)
526 # Units for the timer are in milliseconds
527 kernel.timer.start(1000*kernel._poll_interval)
528 start_event_loop_qt4(kernel.app)
516 529
517 class WxKernel(Kernel):
518 """A Kernel subclass with Wx support."""
519 530
520 def start(self):
521 """Start a kernel with wx event loop support."""
522
523 import wx
524 from IPython.lib.guisupport import start_event_loop_wx
525
526 doi = self.do_one_iteration
527 # Wx uses milliseconds
528 poll_interval = int(1000*self._poll_interval)
529
530 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
531 # We make the Frame hidden when we create it in the main app below.
532 class TimerFrame(wx.Frame):
533 def __init__(self, func):
534 wx.Frame.__init__(self, None, -1)
535 self.timer = wx.Timer(self)
536 # Units for the timer are in milliseconds
537 self.timer.Start(poll_interval)
538 self.Bind(wx.EVT_TIMER, self.on_timer)
539 self.func = func
540
541 def on_timer(self, event):
542 self.func()
543
544 # We need a custom wx.App to create our Frame subclass that has the
545 # wx.Timer to drive the ZMQ event loop.
546 class IPWxApp(wx.App):
547 def OnInit(self):
548 self.frame = TimerFrame(doi)
549 self.frame.Show(False)
550 return True
551
552 # The redirect=False here makes sure that wx doesn't replace
553 # sys.stdout/stderr with its own classes.
554 self.app = IPWxApp(redirect=False)
555 start_event_loop_wx(self.app)
556
557
558 class TkKernel(Kernel):
559 """A Kernel subclass with Tk support."""
531 def loop_wx(kernel):
532 """Start a kernel with wx event loop support."""
560 533
561 def start(self):
562 """Start a Tk enabled event loop."""
534 import wx
535 from IPython.lib.guisupport import start_event_loop_wx
563 536
564 import Tkinter
565 doi = self.do_one_iteration
566 # Tk uses milliseconds
567 poll_interval = int(1000*self._poll_interval)
568 # For Tkinter, we create a Tk object and call its withdraw method.
569 class Timer(object):
570 def __init__(self, func):
571 self.app = Tkinter.Tk()
572 self.app.withdraw()
573 self.func = func
537 doi = kernel.do_one_iteration
538 # Wx uses milliseconds
539 poll_interval = int(1000*kernel._poll_interval)
574 540
575 def on_timer(self):
576 self.func()
577 self.app.after(poll_interval, self.on_timer)
541 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
542 # We make the Frame hidden when we create it in the main app below.
543 class TimerFrame(wx.Frame):
544 def __init__(self, func):
545 wx.Frame.__init__(self, None, -1)
546 self.timer = wx.Timer(self)
547 # Units for the timer are in milliseconds
548 self.timer.Start(poll_interval)
549 self.Bind(wx.EVT_TIMER, self.on_timer)
550 self.func = func
578 551
579 def start(self):
580 self.on_timer() # Call it once to get things going.
581 self.app.mainloop()
552 def on_timer(self, event):
553 self.func()
582 554
583 self.timer = Timer(doi)
584 self.timer.start()
555 # We need a custom wx.App to create our Frame subclass that has the
556 # wx.Timer to drive the ZMQ event loop.
557 class IPWxApp(wx.App):
558 def OnInit(self):
559 self.frame = TimerFrame(doi)
560 self.frame.Show(False)
561 return True
585 562
563 # The redirect=False here makes sure that wx doesn't replace
564 # sys.stdout/stderr with its own classes.
565 kernel.app = IPWxApp(redirect=False)
566 start_event_loop_wx(kernel.app)
586 567
587 class GTKKernel(Kernel):
588 """A Kernel subclass with GTK support."""
589 568
590 def start(self):
591 """Start the kernel, coordinating with the GTK event loop"""
592 from .gui.gtkembed import GTKEmbed
569 def loop_tk(kernel):
570 """Start a kernel with the Tk event loop."""
571
572 import Tkinter
573 doi = kernel.do_one_iteration
574 # Tk uses milliseconds
575 poll_interval = int(1000*kernel._poll_interval)
576 # For Tkinter, we create a Tk object and call its withdraw method.
577 class Timer(object):
578 def __init__(self, func):
579 self.app = Tkinter.Tk()
580 self.app.withdraw()
581 self.func = func
593 582
594 gtk_kernel = GTKEmbed(self)
595 gtk_kernel.start()
583 def on_timer(self):
584 self.func()
585 self.app.after(poll_interval, self.on_timer)
596 586
587 def start(self):
588 self.on_timer() # Call it once to get things going.
589 self.app.mainloop()
597 590
598 class OSXKernel(TkKernel):
599 """A Kernel subclass with Cocoa support via the matplotlib OSX backend."""
591 kernel.timer = Timer(doi)
592 kernel.timer.start()
593
594
595 def loop_gtk(kernel):
596 """Start the kernel, coordinating with the GTK event loop"""
597 from .gui.gtkembed import GTKEmbed
598
599 gtk_kernel = GTKEmbed(kernel)
600 gtk_kernel.start()
601
602
603 def loop_cocoa(kernel):
604 """Start the kernel, coordinating with the Cocoa CFRunLoop event loop
605 via the matplotlib MacOSX backend.
606 """
607 import matplotlib
608 if matplotlib.__version__ < '1.1.0':
609 kernel.log.warn(
610 "MacOSX backend in matplotlib %s doesn't have a Timer, "
611 "falling back on Tk for CFRunLoop integration. Note that "
612 "even this won't work if Tk is linked against X11 instead of "
613 "Cocoa (e.g. EPD). To use the MacOSX backend in the kernel, "
614 "you must use matplotlib >= 1.1.0, or a native libtk."
615 )
616 return loop_tk(kernel)
600 617
601 def start(self):
602 """Start the kernel, coordinating with the Cocoa CFRunLoop event loop
603 via the matplotlib MacOSX backend.
604 """
605 import matplotlib
606 if matplotlib.__version__ < '1.1.0':
607 self.log.warn(
608 "MacOSX backend in matplotlib %s doesn't have a Timer, "
609 "falling back on Tk for CFRunLoop integration. Note that "
610 "even this won't work if Tk is linked against X11 instead of "
611 "Cocoa (e.g. EPD). To use the MacOSX backend in the kernel, "
612 "you must use matplotlib >= 1.1.0, or a native libtk."
613 )
614 return TkKernel.start(self)
615
616 from matplotlib.backends.backend_macosx import TimerMac, show
617
618 # scale interval for sec->ms
619 poll_interval = int(1000*self._poll_interval)
620
621 real_excepthook = sys.excepthook
622 def handle_int(etype, value, tb):
623 """don't let KeyboardInterrupts look like crashes"""
624 if etype is KeyboardInterrupt:
625 io.raw_print("KeyboardInterrupt caught in CFRunLoop")
626 else:
627 real_excepthook(etype, value, tb)
628
629 # add doi() as a Timer to the CFRunLoop
630 def doi():
631 # restore excepthook during IPython code
632 sys.excepthook = real_excepthook
633 self.do_one_iteration()
634 # and back:
635 sys.excepthook = handle_int
636
637 t = TimerMac(poll_interval)
638 t.add_callback(doi)
639 t.start()
640
641 # but still need a Poller for when there are no active windows,
642 # during which time mainloop() returns immediately
643 poller = zmq.Poller()
644 poller.register(self.shell_socket, zmq.POLLIN)
645
646 while True:
618 from matplotlib.backends.backend_macosx import TimerMac, show
619
620 # scale interval for sec->ms
621 poll_interval = int(1000*kernel._poll_interval)
622
623 real_excepthook = sys.excepthook
624 def handle_int(etype, value, tb):
625 """don't let KeyboardInterrupts look like crashes"""
626 if etype is KeyboardInterrupt:
627 io.raw_print("KeyboardInterrupt caught in CFRunLoop")
628 else:
629 real_excepthook(etype, value, tb)
630
631 # add doi() as a Timer to the CFRunLoop
632 def doi():
633 # restore excepthook during IPython code
634 sys.excepthook = real_excepthook
635 kernel.do_one_iteration()
636 # and back:
637 sys.excepthook = handle_int
638
639 t = TimerMac(poll_interval)
640 t.add_callback(doi)
641 t.start()
642
643 # but still need a Poller for when there are no active windows,
644 # during which time mainloop() returns immediately
645 poller = zmq.Poller()
646 poller.register(kernel.shell_socket, zmq.POLLIN)
647
648 while True:
649 try:
650 # double nested try/except, to properly catch KeyboardInterrupt
651 # due to pyzmq Issue #130
647 652 try:
648 # double nested try/except, to properly catch KeyboardInterrupt
649 # due to pyzmq Issue #130
650 try:
651 # don't let interrupts during mainloop invoke crash_handler:
652 sys.excepthook = handle_int
653 show.mainloop()
654 sys.excepthook = real_excepthook
655 # use poller if mainloop returned (no windows)
656 # scale by extra factor of 10, since it's a real poll
657 poller.poll(10*poll_interval)
658 self.do_one_iteration()
659 except:
660 raise
661 except KeyboardInterrupt:
662 # Ctrl-C shouldn't crash the kernel
663 io.raw_print("KeyboardInterrupt caught in kernel")
664 finally:
665 # ensure excepthook is restored
653 # don't let interrupts during mainloop invoke crash_handler:
654 sys.excepthook = handle_int
655 show.mainloop()
666 656 sys.excepthook = real_excepthook
657 # use poller if mainloop returned (no windows)
658 # scale by extra factor of 10, since it's a real poll
659 poller.poll(10*poll_interval)
660 kernel.do_one_iteration()
661 except:
662 raise
663 except KeyboardInterrupt:
664 # Ctrl-C shouldn't crash the kernel
665 io.raw_print("KeyboardInterrupt caught in kernel")
666 finally:
667 # ensure excepthook is restored
668 sys.excepthook = real_excepthook
669
670 # mapping of keys to loop functions
671 loop_map = {
672 'qt' : loop_qt4,
673 'qt4': loop_qt4,
674 'inline': None,
675 'osx': loop_cocoa,
676 'wx' : loop_wx,
677 'tk' : loop_tk,
678 'gtk': loop_gtk,
679 }
680
681 def enable_gui(gui, kernel=None):
682 """Enable integration with a give GUI"""
683 if kernel is None:
684 kernel = IPKernelApp.instance().kernel
685 if gui not in loop_map:
686 raise ValueError("GUI %r not supported" % gui)
687 loop = loop_map[gui]
688 if kernel.eventloop is not None and kernel.eventloop is not loop:
689 raise RuntimeError("Cannot activate multiple GUI eventloops")
690 kernel.eventloop = loop
667 691
668 692
669 693 #-----------------------------------------------------------------------------
670 694 # Aliases and Flags for the IPKernelApp
671 695 #-----------------------------------------------------------------------------
672 696
673 697 flags = dict(kernel_flags)
674 698 flags.update(shell_flags)
675 699
676 700 addflag = lambda *args: flags.update(boolean_flag(*args))
677 701
678 702 flags['pylab'] = (
679 703 {'IPKernelApp' : {'pylab' : 'auto'}},
680 704 """Pre-load matplotlib and numpy for interactive use with
681 705 the default matplotlib backend."""
682 706 )
683 707
684 708 aliases = dict(kernel_aliases)
685 709 aliases.update(shell_aliases)
686 710
687 711 # it's possible we don't want short aliases for *all* of these:
688 712 aliases.update(dict(
689 713 pylab='IPKernelApp.pylab',
690 714 ))
691 715
692 716 #-----------------------------------------------------------------------------
693 717 # The IPKernelApp class
694 718 #-----------------------------------------------------------------------------
695 719
696 720 class IPKernelApp(KernelApp, InteractiveShellApp):
697 721 name = 'ipkernel'
698 722
699 723 aliases = Dict(aliases)
700 724 flags = Dict(flags)
701 725 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
702 726 # configurables
703 727 pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'inline', 'auto'],
704 728 config=True,
705 729 help="""Pre-load matplotlib and numpy for interactive use,
706 730 selecting a particular matplotlib backend and loop integration.
707 731 """
708 732 )
709 733 def initialize(self, argv=None):
710 734 super(IPKernelApp, self).initialize(argv)
711 735 self.init_shell()
712 736 self.init_extensions()
713 737 self.init_code()
714 738
715 739 def init_kernel(self):
716 740 kernel_factory = Kernel
717 741
718 kernel_map = {
719 'qt' : QtKernel,
720 'qt4': QtKernel,
721 'inline': Kernel,
722 'osx': OSXKernel,
723 'wx' : WxKernel,
724 'tk' : TkKernel,
725 'gtk': GTKKernel,
726 }
727
728 742 if self.pylab:
729 743 key = None if self.pylab == 'auto' else self.pylab
730 744 gui, backend = pylabtools.find_gui_and_backend(key)
731 kernel_factory = kernel_map.get(gui)
732 if kernel_factory is None:
733 raise ValueError('GUI is not supported: %r' % gui)
734 pylabtools.activate_matplotlib(backend)
735 745
736 746 kernel = kernel_factory(config=self.config, session=self.session,
737 747 shell_socket=self.shell_socket,
738 748 iopub_socket=self.iopub_socket,
739 749 stdin_socket=self.stdin_socket,
740 log=self.log
750 log=self.log,
741 751 )
742 752 self.kernel = kernel
743 753 kernel.record_ports(self.ports)
744 754
745 755 if self.pylab:
746 import_all = self.pylab_import_all
747 pylabtools.import_pylab(kernel.shell.user_ns, backend, import_all,
748 shell=kernel.shell)
756 kernel.shell.enable_pylab(gui, import_all=self.pylab_import_all)
749 757
750 758 def init_shell(self):
751 759 self.shell = self.kernel.shell
752 760
753 761
754 762 #-----------------------------------------------------------------------------
755 763 # Kernel main and launch functions
756 764 #-----------------------------------------------------------------------------
757 765
758 766 def launch_kernel(*args, **kwargs):
759 767 """Launches a localhost IPython kernel, binding to the specified ports.
760 768
761 769 This function simply calls entry_point.base_launch_kernel with the right first
762 770 command to start an ipkernel. See base_launch_kernel for arguments.
763 771
764 772 Returns
765 773 -------
766 774 A tuple of form:
767 775 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
768 776 where kernel_process is a Popen object and the ports are integers.
769 777 """
770 778 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
771 779 *args, **kwargs)
772 780
773 781
774 782 def main():
775 783 """Run an IPKernel as an application"""
776 784 app = IPKernelApp.instance()
777 785 app.initialize()
778 786 app.start()
779 787
780 788
781 789 if __name__ == '__main__':
782 790 main()
@@ -1,495 +1,548 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 sys
22 22 from subprocess import Popen, PIPE
23 23
24 24 # Our own
25 25 from IPython.core.interactiveshell import (
26 26 InteractiveShell, InteractiveShellABC
27 27 )
28 28 from IPython.core import page
29 29 from IPython.core.autocall import ZMQExitAutocall
30 30 from IPython.core.displaypub import DisplayPublisher
31 31 from IPython.core.macro import Macro
32 32 from IPython.core.magic import MacroToEdit
33 33 from IPython.core.payloadpage import install_payload_page
34 from IPython.lib import pylabtools
34 35 from IPython.lib.kernel import (
35 36 get_connection_file, get_connection_info, connect_qtconsole
36 37 )
37 38 from IPython.utils import io
38 39 from IPython.utils.jsonutil import json_clean
39 40 from IPython.utils.path import get_py_filename
40 41 from IPython.utils.process import arg_split
41 42 from IPython.utils.traitlets import Instance, Type, Dict, CBool
42 43 from IPython.utils.warn import warn, error
43 44 from IPython.zmq.displayhook import ZMQShellDisplayHook, _encode_binary
44 45 from IPython.zmq.session import extract_header
45 46 from session import Session
46 47
47 48 #-----------------------------------------------------------------------------
48 49 # Globals and side-effects
49 50 #-----------------------------------------------------------------------------
50 51
51 52 # Install the payload version of page.
52 53 install_payload_page()
53 54
54 55 #-----------------------------------------------------------------------------
55 56 # Functions and classes
56 57 #-----------------------------------------------------------------------------
57 58
58 59 class ZMQDisplayPublisher(DisplayPublisher):
59 60 """A display publisher that publishes data using a ZeroMQ PUB socket."""
60 61
61 62 session = Instance(Session)
62 63 pub_socket = Instance('zmq.Socket')
63 64 parent_header = Dict({})
64 65
65 66 def set_parent(self, parent):
66 67 """Set the parent for outbound messages."""
67 68 self.parent_header = extract_header(parent)
68 69
69 70 def publish(self, source, data, metadata=None):
70 71 if metadata is None:
71 72 metadata = {}
72 73 self._validate_data(source, data, metadata)
73 74 content = {}
74 75 content['source'] = source
75 76 _encode_binary(data)
76 77 content['data'] = data
77 78 content['metadata'] = metadata
78 79 self.session.send(
79 80 self.pub_socket, u'display_data', json_clean(content),
80 81 parent=self.parent_header
81 82 )
82 83
83 84 def clear_output(self, stdout=True, stderr=True, other=True):
84 85 content = dict(stdout=stdout, stderr=stderr, other=other)
85 86 self.session.send(
86 87 self.pub_socket, u'clear_output', content,
87 88 parent=self.parent_header
88 89 )
89 90
90 91 class ZMQInteractiveShell(InteractiveShell):
91 92 """A subclass of InteractiveShell for ZMQ."""
92 93
93 94 displayhook_class = Type(ZMQShellDisplayHook)
94 95 display_pub_class = Type(ZMQDisplayPublisher)
95 96
96 97 # Override the traitlet in the parent class, because there's no point using
97 98 # readline for the kernel. Can be removed when the readline code is moved
98 99 # to the terminal frontend.
99 100 colors_force = CBool(True)
100 101 readline_use = CBool(False)
101 102 # autoindent has no meaning in a zmqshell, and attempting to enable it
102 103 # will print a warning in the absence of readline.
103 104 autoindent = CBool(False)
104 105
105 106 exiter = Instance(ZMQExitAutocall)
106 107 def _exiter_default(self):
107 108 return ZMQExitAutocall(self)
108 109
109 110 keepkernel_on_exit = None
110 111
111 112 def init_environment(self):
112 113 """Configure the user's environment.
113 114
114 115 """
115 116 env = os.environ
116 117 # These two ensure 'ls' produces nice coloring on BSD-derived systems
117 118 env['TERM'] = 'xterm-color'
118 119 env['CLICOLOR'] = '1'
119 120 # Since normal pagers don't work at all (over pexpect we don't have
120 121 # single-key control of the subprocess), try to disable paging in
121 122 # subprocesses as much as possible.
122 123 env['PAGER'] = 'cat'
123 124 env['GIT_PAGER'] = 'cat'
124 125
125 126 def auto_rewrite_input(self, cmd):
126 127 """Called to show the auto-rewritten input for autocall and friends.
127 128
128 129 FIXME: this payload is currently not correctly processed by the
129 130 frontend.
130 131 """
131 132 new = self.displayhook.prompt1.auto_rewrite() + cmd
132 133 payload = dict(
133 134 source='IPython.zmq.zmqshell.ZMQInteractiveShell.auto_rewrite_input',
134 135 transformed_input=new,
135 136 )
136 137 self.payload_manager.write_payload(payload)
137 138
138 139 def ask_exit(self):
139 140 """Engage the exit actions."""
140 141 payload = dict(
141 142 source='IPython.zmq.zmqshell.ZMQInteractiveShell.ask_exit',
142 143 exit=True,
143 144 keepkernel=self.keepkernel_on_exit,
144 145 )
145 146 self.payload_manager.write_payload(payload)
146 147
147 148 def _showtraceback(self, etype, evalue, stb):
148 149
149 150 exc_content = {
150 151 u'traceback' : stb,
151 152 u'ename' : unicode(etype.__name__),
152 153 u'evalue' : unicode(evalue)
153 154 }
154 155
155 156 dh = self.displayhook
156 157 # Send exception info over pub socket for other clients than the caller
157 158 # to pick up
158 159 exc_msg = dh.session.send(dh.pub_socket, u'pyerr', json_clean(exc_content), dh.parent_header)
159 160
160 161 # FIXME - Hack: store exception info in shell object. Right now, the
161 162 # caller is reading this info after the fact, we need to fix this logic
162 163 # to remove this hack. Even uglier, we need to store the error status
163 164 # here, because in the main loop, the logic that sets it is being
164 165 # skipped because runlines swallows the exceptions.
165 166 exc_content[u'status'] = u'error'
166 167 self._reply_content = exc_content
167 168 # /FIXME
168 169
169 170 return exc_content
170 171
171 172 #------------------------------------------------------------------------
172 173 # Magic overrides
173 174 #------------------------------------------------------------------------
174 175 # Once the base class stops inheriting from magic, this code needs to be
175 176 # moved into a separate machinery as well. For now, at least isolate here
176 177 # the magics which this class needs to implement differently from the base
177 178 # class, or that are unique to it.
178 179
179 180 def magic_doctest_mode(self,parameter_s=''):
180 181 """Toggle doctest mode on and off.
181 182
182 183 This mode is intended to make IPython behave as much as possible like a
183 184 plain Python shell, from the perspective of how its prompts, exceptions
184 185 and output look. This makes it easy to copy and paste parts of a
185 186 session into doctests. It does so by:
186 187
187 188 - Changing the prompts to the classic ``>>>`` ones.
188 189 - Changing the exception reporting mode to 'Plain'.
189 190 - Disabling pretty-printing of output.
190 191
191 192 Note that IPython also supports the pasting of code snippets that have
192 193 leading '>>>' and '...' prompts in them. This means that you can paste
193 194 doctests from files or docstrings (even if they have leading
194 195 whitespace), and the code will execute correctly. You can then use
195 196 '%history -t' to see the translated history; this will give you the
196 197 input after removal of all the leading prompts and whitespace, which
197 198 can be pasted back into an editor.
198 199
199 200 With these features, you can switch into this mode easily whenever you
200 201 need to do testing and changes to doctests, without having to leave
201 202 your existing IPython session.
202 203 """
203 204
204 205 from IPython.utils.ipstruct import Struct
205 206
206 207 # Shorthands
207 208 shell = self.shell
208 209 disp_formatter = self.shell.display_formatter
209 210 ptformatter = disp_formatter.formatters['text/plain']
210 211 # dstore is a data store kept in the instance metadata bag to track any
211 212 # changes we make, so we can undo them later.
212 213 dstore = shell.meta.setdefault('doctest_mode', Struct())
213 214 save_dstore = dstore.setdefault
214 215
215 216 # save a few values we'll need to recover later
216 217 mode = save_dstore('mode', False)
217 218 save_dstore('rc_pprint', ptformatter.pprint)
218 219 save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
219 220 save_dstore('xmode', shell.InteractiveTB.mode)
220 221
221 222 if mode == False:
222 223 # turn on
223 224 ptformatter.pprint = False
224 225 disp_formatter.plain_text_only = True
225 226 shell.magic_xmode('Plain')
226 227 else:
227 228 # turn off
228 229 ptformatter.pprint = dstore.rc_pprint
229 230 disp_formatter.plain_text_only = dstore.rc_plain_text_only
230 231 shell.magic_xmode(dstore.xmode)
231 232
232 233 # Store new mode and inform on console
233 234 dstore.mode = bool(1-int(mode))
234 235 mode_label = ['OFF','ON'][dstore.mode]
235 236 print('Doctest mode is:', mode_label)
236 237
237 238 # Send the payload back so that clients can modify their prompt display
238 239 payload = dict(
239 240 source='IPython.zmq.zmqshell.ZMQInteractiveShell.magic_doctest_mode',
240 241 mode=dstore.mode)
241 242 self.payload_manager.write_payload(payload)
242 243
243 244 def magic_edit(self,parameter_s='',last_call=['','']):
244 245 """Bring up an editor and execute the resulting code.
245 246
246 247 Usage:
247 248 %edit [options] [args]
248 249
249 250 %edit runs an external text editor. You will need to set the command for
250 251 this editor via the ``TerminalInteractiveShell.editor`` option in your
251 252 configuration file before it will work.
252 253
253 254 This command allows you to conveniently edit multi-line code right in
254 255 your IPython session.
255 256
256 257 If called without arguments, %edit opens up an empty editor with a
257 258 temporary file and will execute the contents of this file when you
258 259 close it (don't forget to save it!).
259 260
260 261
261 262 Options:
262 263
263 264 -n <number>: open the editor at a specified line number. By default,
264 265 the IPython editor hook uses the unix syntax 'editor +N filename', but
265 266 you can configure this by providing your own modified hook if your
266 267 favorite editor supports line-number specifications with a different
267 268 syntax.
268 269
269 270 -p: this will call the editor with the same data as the previous time
270 271 it was used, regardless of how long ago (in your current session) it
271 272 was.
272 273
273 274 -r: use 'raw' input. This option only applies to input taken from the
274 275 user's history. By default, the 'processed' history is used, so that
275 276 magics are loaded in their transformed version to valid Python. If
276 277 this option is given, the raw input as typed as the command line is
277 278 used instead. When you exit the editor, it will be executed by
278 279 IPython's own processor.
279 280
280 281 -x: do not execute the edited code immediately upon exit. This is
281 282 mainly useful if you are editing programs which need to be called with
282 283 command line arguments, which you can then do using %run.
283 284
284 285
285 286 Arguments:
286 287
287 288 If arguments are given, the following possibilites exist:
288 289
289 290 - The arguments are numbers or pairs of colon-separated numbers (like
290 291 1 4:8 9). These are interpreted as lines of previous input to be
291 292 loaded into the editor. The syntax is the same of the %macro command.
292 293
293 294 - If the argument doesn't start with a number, it is evaluated as a
294 295 variable and its contents loaded into the editor. You can thus edit
295 296 any string which contains python code (including the result of
296 297 previous edits).
297 298
298 299 - If the argument is the name of an object (other than a string),
299 300 IPython will try to locate the file where it was defined and open the
300 301 editor at the point where it is defined. You can use `%edit function`
301 302 to load an editor exactly at the point where 'function' is defined,
302 303 edit it and have the file be executed automatically.
303 304
304 305 If the object is a macro (see %macro for details), this opens up your
305 306 specified editor with a temporary file containing the macro's data.
306 307 Upon exit, the macro is reloaded with the contents of the file.
307 308
308 309 Note: opening at an exact line is only supported under Unix, and some
309 310 editors (like kedit and gedit up to Gnome 2.8) do not understand the
310 311 '+NUMBER' parameter necessary for this feature. Good editors like
311 312 (X)Emacs, vi, jed, pico and joe all do.
312 313
313 314 - If the argument is not found as a variable, IPython will look for a
314 315 file with that name (adding .py if necessary) and load it into the
315 316 editor. It will execute its contents with execfile() when you exit,
316 317 loading any code in the file into your interactive namespace.
317 318
318 319 After executing your code, %edit will return as output the code you
319 320 typed in the editor (except when it was an existing file). This way
320 321 you can reload the code in further invocations of %edit as a variable,
321 322 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
322 323 the output.
323 324
324 325 Note that %edit is also available through the alias %ed.
325 326
326 327 This is an example of creating a simple function inside the editor and
327 328 then modifying it. First, start up the editor:
328 329
329 330 In [1]: ed
330 331 Editing... done. Executing edited code...
331 332 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
332 333
333 334 We can then call the function foo():
334 335
335 336 In [2]: foo()
336 337 foo() was defined in an editing session
337 338
338 339 Now we edit foo. IPython automatically loads the editor with the
339 340 (temporary) file where foo() was previously defined:
340 341
341 342 In [3]: ed foo
342 343 Editing... done. Executing edited code...
343 344
344 345 And if we call foo() again we get the modified version:
345 346
346 347 In [4]: foo()
347 348 foo() has now been changed!
348 349
349 350 Here is an example of how to edit a code snippet successive
350 351 times. First we call the editor:
351 352
352 353 In [5]: ed
353 354 Editing... done. Executing edited code...
354 355 hello
355 356 Out[5]: "print 'hello'n"
356 357
357 358 Now we call it again with the previous output (stored in _):
358 359
359 360 In [6]: ed _
360 361 Editing... done. Executing edited code...
361 362 hello world
362 363 Out[6]: "print 'hello world'n"
363 364
364 365 Now we call it with the output #8 (stored in _8, also as Out[8]):
365 366
366 367 In [7]: ed _8
367 368 Editing... done. Executing edited code...
368 369 hello again
369 370 Out[7]: "print 'hello again'n"
370 371 """
371 372
372 373 opts,args = self.parse_options(parameter_s,'prn:')
373 374
374 375 try:
375 376 filename, lineno, _ = self._find_edit_target(args, opts, last_call)
376 377 except MacroToEdit as e:
377 378 # TODO: Implement macro editing over 2 processes.
378 379 print("Macro editing not yet implemented in 2-process model.")
379 380 return
380 381
381 382 # Make sure we send to the client an absolute path, in case the working
382 383 # directory of client and kernel don't match
383 384 filename = os.path.abspath(filename)
384 385
385 386 payload = {
386 387 'source' : 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic',
387 388 'filename' : filename,
388 389 'line_number' : lineno
389 390 }
390 391 self.payload_manager.write_payload(payload)
391 392
392 def magic_gui(self, *args, **kwargs):
393 raise NotImplementedError(
394 'Kernel GUI support is not implemented yet, except for --pylab.')
393 def magic_gui(self, parameter_s=''):
394 """Enable or disable IPython GUI event loop integration.
395
396 %gui [GUINAME]
397
398 This magic replaces IPython's threaded shells that were activated
399 using the (pylab/wthread/etc.) command line flags. GUI toolkits
400 can now be enabled at runtime and keyboard
401 interrupts should work without any problems. The following toolkits
402 are supported: wxPython, PyQt4, PyGTK, Cocoa, and Tk::
403
404 %gui wx # enable wxPython event loop integration
405 %gui qt4|qt # enable PyQt4 event loop integration
406 %gui gtk # enable PyGTK event loop integration
407 %gui OSX # enable Cocoa event loop integration (requires matplotlib 1.1)
408 %gui tk # enable Tk event loop integration
409
410 WARNING: after any of these has been called you can simply create
411 an application object, but DO NOT start the event loop yourself, as
412 we have already handled that.
413 """
414 from IPython.zmq.ipkernel import enable_gui
415 opts, arg = self.parse_options(parameter_s, '')
416 if arg=='': arg = None
417 return enable_gui(arg)
418
419 def enable_pylab(self, gui=None, import_all=True):
420 """Activate pylab support at runtime.
421
422 This turns on support for matplotlib, preloads into the interactive
423 namespace all of numpy and pylab, and configures IPython to correcdtly
424 interact with the GUI event loop. The GUI backend to be used can be
425 optionally selected with the optional :param:`gui` argument.
426
427 Parameters
428 ----------
429 gui : optional, string [default: inline]
430
431 If given, dictates the choice of matplotlib GUI backend to use
432 (should be one of IPython's supported backends, 'inline', 'qt', 'osx',
433 'tk', or 'gtk'), otherwise we use the default chosen by matplotlib
434 (as dictated by the matplotlib build-time options plus the user's
435 matplotlibrc configuration file).
436 """
437 from IPython.zmq.ipkernel import enable_gui
438 # We want to prevent the loading of pylab to pollute the user's
439 # namespace as shown by the %who* magics, so we execute the activation
440 # code in an empty namespace, and we update *both* user_ns and
441 # user_ns_hidden with this information.
442 ns = {}
443 # override default to inline, from auto-detect
444 gui = pylabtools.pylab_activate(ns, gui or 'inline', import_all, self)
445 self.user_ns.update(ns)
446 self.user_ns_hidden.update(ns)
447 # Now we must activate the gui pylab wants to use, and fix %run to take
448 # plot updates into account
449 enable_gui(gui)
450 self.magic_run = self._pylab_magic_run
395 451
396 def magic_pylab(self, *args, **kwargs):
397 raise NotImplementedError(
398 'pylab support must be enabled in command line options.')
399 452
400 453 # A few magics that are adapted to the specifics of using pexpect and a
401 454 # remote terminal
402 455
403 456 def magic_clear(self, arg_s):
404 457 """Clear the terminal."""
405 458 if os.name == 'posix':
406 459 self.shell.system("clear")
407 460 else:
408 461 self.shell.system("cls")
409 462
410 463 if os.name == 'nt':
411 464 # This is the usual name in windows
412 465 magic_cls = magic_clear
413 466
414 467 # Terminal pagers won't work over pexpect, but we do have our own pager
415 468
416 469 def magic_less(self, arg_s):
417 470 """Show a file through the pager.
418 471
419 472 Files ending in .py are syntax-highlighted."""
420 473 cont = open(arg_s).read()
421 474 if arg_s.endswith('.py'):
422 475 cont = self.shell.pycolorize(cont)
423 476 page.page(cont)
424 477
425 478 magic_more = magic_less
426 479
427 480 # Man calls a pager, so we also need to redefine it
428 481 if os.name == 'posix':
429 482 def magic_man(self, arg_s):
430 483 """Find the man page for the given command and display in pager."""
431 484 page.page(self.shell.getoutput('man %s | col -b' % arg_s,
432 485 split=False))
433 486
434 487 # FIXME: this is specific to the GUI, so we should let the gui app load
435 488 # magics at startup that are only for the gui. Once the gui app has proper
436 489 # profile and configuration management, we can have it initialize a kernel
437 490 # with a special config file that provides these.
438 491 def magic_guiref(self, arg_s):
439 492 """Show a basic reference about the GUI console."""
440 493 from IPython.core.usage import gui_reference
441 494 page.page(gui_reference, auto_html=True)
442 495
443 496 def magic_connect_info(self, arg_s):
444 497 """Print information for connecting other clients to this kernel
445 498
446 499 It will print the contents of this session's connection file, as well as
447 500 shortcuts for local clients.
448 501
449 502 In the simplest case, when called from the most recently launched kernel,
450 503 secondary clients can be connected, simply with:
451 504
452 505 $> ipython <app> --existing
453 506
454 507 """
455 508 try:
456 509 connection_file = get_connection_file()
457 510 info = get_connection_info(unpack=False)
458 511 except Exception as e:
459 512 error("Could not get connection info: %r" % e)
460 513 return
461 514
462 515 print (info + '\n')
463 516 print ("Paste the above JSON into a file, and connect with:\n"
464 517 " $> ipython <app> --existing <file>\n"
465 518 "or, if you are local, you can connect with just:\n"
466 519 " $> ipython <app> --existing %s\n"
467 520 "or even just:\n"
468 521 " $> ipython <app> --existing\n"
469 522 "if this is the most recent IPython session you have started."
470 523 % os.path.basename(connection_file)
471 524 )
472 525
473 526 def magic_qtconsole(self, arg_s):
474 527 """Open a qtconsole connected to this kernel.
475 528
476 529 Useful for connecting a qtconsole to running notebooks, for better
477 530 debugging.
478 531 """
479 532 try:
480 533 p = connect_qtconsole(argv=arg_split(arg_s, os.name=='posix'))
481 534 except Exception as e:
482 535 error("Could not start qtconsole: %r" % e)
483 536 return
484 537
485 538
486 539 def set_next_input(self, text):
487 540 """Send the specified text to the frontend to be presented at the next
488 541 input cell."""
489 542 payload = dict(
490 543 source='IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input',
491 544 text=text
492 545 )
493 546 self.payload_manager.write_payload(payload)
494 547
495 548 InteractiveShellABC.register(ZMQInteractiveShell)
General Comments 0
You need to be logged in to leave comments. Login now