##// END OF EJS Templates
Initial checkin of (not yet working) matplotlib payload backend and associated machinery.
epatters -
Show More
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
@@ -0,0 +1,23 b''
1 """ Provides basic funtionality for payload backends.
2 """
3
4 # Local imports.
5 from IPython.zmq.kernel import Kernel
6
7
8 def add_plot_payload(format, data, metadata={}):
9 """ Add a plot payload to the current execution reply.
10
11 Parameters:
12 -----------
13 format : str
14 Identifies the format of the plot data.
15
16 data : str
17 The raw plot data.
18
19 metadata : dict, optional [default empty]
20 Allows for specification of additional information about the plot data.
21 """
22 payload = dict(format=format, data=data, metadata=metadata)
23 Kernel.get_kernel().add_exec_payload('plot', payload)
@@ -0,0 +1,25 b''
1 # Standard library imports
2 from cStringIO import StringIO
3
4 # System library imports.
5 from matplotlib.backends.backend_svg import new_figure_manager
6 from matplotlib._pylab_helpers import Gcf
7
8 # Local imports.
9 from backend_payload import add_plot_payload
10
11
12 def show():
13 """ Deliver a SVG payload.
14 """
15 figure_manager = Gcf.get_actve()
16 if figure_manager is not None:
17 data = svg_from_canvas(figure_manager.canvas)
18 add_plot_payload('svg', data)
19
20 def svg_from_canvas(canvas):
21 """ Return a string containing the SVG representation of a FigureCanvasSvg.
22 """
23 string_io = StringIO()
24 canvas.print_svg(string_io)
25 return string_io.getvalue()
@@ -2,11 +2,14 b''
2 """
2 """
3
3
4 # System library imports.
4 # System library imports.
5 from PyQt4 import QtCore
5 from PyQt4 import QtCore, QtGui
6
6
7 # IPython imports.
7 # IPython imports.
8 from IPython.utils.traitlets import HasTraits
8 from IPython.utils.traitlets import HasTraits
9
9
10 #-----------------------------------------------------------------------------
11 # Metaclasses
12 #-----------------------------------------------------------------------------
10
13
11 MetaHasTraits = type(HasTraits)
14 MetaHasTraits = type(HasTraits)
12 MetaQObject = type(QtCore.QObject)
15 MetaQObject = type(QtCore.QObject)
@@ -19,4 +22,42 b' class MetaQObjectHasTraits(MetaQObject, MetaHasTraits):'
19 QObject. See QtKernelManager for an example.
22 QObject. See QtKernelManager for an example.
20 """
23 """
21 pass
24 pass
25
26 #-----------------------------------------------------------------------------
27 # Functions
28 #-----------------------------------------------------------------------------
22
29
30 def image_from_svg(string, size=None):
31 """ Convert a string containing SVG data into a QImage.
32
33 Parameters:
34 -----------
35 string : str
36 A Python string containing the SVG data.
37
38 size : QSize or None [default None]
39 The size of the image that is produced. If not specified, the SVG data's
40 default size is used.
41
42 Raises:
43 -------
44 ValueError
45 If an invalid SVG string is provided.
46
47 Returns:
48 --------
49 A QImage with format QImage.Format_ARGB32_Premultiplied.
50 """
51 from PyQt4 import QtSvg
52
53 bytes = QtCore.QByteArray(string)
54 renderer = QtSvg.QSvgRenderer(bytes)
55 if not renderer.isValid():
56 raise ValueError('Invalid SVG data.')
57
58 if size is None:
59 size = renderer.defaultSize()
60 image = QtGui.QImage(size, QtGui.QImage.Format_ARGB32_Premultiplied)
61 painter = QtGui.QPainter(image)
62 renderer.render(painter)
63 return image
@@ -114,13 +114,11 b' class DisplayHook(object):'
114 self.parent_header = {}
114 self.parent_header = {}
115
115
116 def __call__(self, obj):
116 def __call__(self, obj):
117 if obj is None:
117 if obj is not None:
118 return
118 __builtin__._ = obj
119
119 msg = self.session.msg(u'pyout', {u'data':repr(obj)},
120 __builtin__._ = obj
120 parent=self.parent_header)
121 msg = self.session.msg(u'pyout', {u'data':repr(obj)},
121 self.pub_socket.send_json(msg)
122 parent=self.parent_header)
123 self.pub_socket.send_json(msg)
124
122
125 def set_parent(self, parent):
123 def set_parent(self, parent):
126 self.parent_header = extract_header(parent)
124 self.parent_header = extract_header(parent)
@@ -128,6 +126,22 b' class DisplayHook(object):'
128
126
129 class Kernel(object):
127 class Kernel(object):
130
128
129 # The global kernel instance.
130 _kernel = None
131
132 # Maps user-friendly backend names to matplotlib backend identifiers.
133 _pylab_map = { 'tk': 'TkAgg',
134 'gtk': 'GTKAgg',
135 'wx': 'WXAgg',
136 'qt': 'Qt4Agg', # qt3 not supported
137 'qt4': 'Qt4Agg',
138 'payload-svg' : \
139 'module://IPython.zmq.pylab.backend_payload_svg' }
140
141 #---------------------------------------------------------------------------
142 # Kernel interface
143 #---------------------------------------------------------------------------
144
131 def __init__(self, session, reply_socket, pub_socket, req_socket):
145 def __init__(self, session, reply_socket, pub_socket, req_socket):
132 self.session = session
146 self.session = session
133 self.reply_socket = reply_socket
147 self.reply_socket = reply_socket
@@ -137,6 +151,9 b' class Kernel(object):'
137 self.history = []
151 self.history = []
138 self.compiler = CommandCompiler()
152 self.compiler = CommandCompiler()
139 self.completer = KernelCompleter(self.user_ns)
153 self.completer = KernelCompleter(self.user_ns)
154
155 # Protected variables.
156 self._exec_payload = {}
140
157
141 # Build dict of handlers for message types
158 # Build dict of handlers for message types
142 msg_types = [ 'execute_request', 'complete_request',
159 msg_types = [ 'execute_request', 'complete_request',
@@ -145,27 +162,83 b' class Kernel(object):'
145 for msg_type in msg_types:
162 for msg_type in msg_types:
146 self.handlers[msg_type] = getattr(self, msg_type)
163 self.handlers[msg_type] = getattr(self, msg_type)
147
164
148 def abort_queue(self):
165 def add_exec_payload(self, key, value):
166 """ Adds a key/value pair to the execute payload.
167 """
168 self._exec_payload[key] = value
169
170 def activate_pylab(self, backend=None, import_all=True):
171 """ Activates pylab in this kernel's namespace.
172
173 Parameters:
174 -----------
175 backend : str, optional
176 A valid backend name.
177
178 import_all : bool, optional
179 If true, an 'import *' is done from numpy and pylab.
180 """
181 # FIXME: This is adapted from IPython.lib.pylabtools.pylab_activate.
182 # Common funtionality should be refactored.
183
184 import matplotlib
185
186 # We must set the desired backend before importing pylab.
187 if backend:
188 matplotlib.use(self._pylab_map[backend])
189
190 # This must be imported last in the matplotlib series, after
191 # backend/interactivity choices have been made.
192 import matplotlib.pylab as pylab
193
194 # Import numpy as np/pyplot as plt are conventions we're trying to
195 # somewhat standardize on. Making them available to users by default
196 # will greatly help this.
197 exec ("import numpy\n"
198 "import matplotlib\n"
199 "from matplotlib import pylab, mlab, pyplot\n"
200 "np = numpy\n"
201 "plt = pyplot\n"
202 ) in self.user_ns
203
204 if import_all:
205 exec("from matplotlib.pylab import *\n"
206 "from numpy import *\n") in self.user_ns
207
208 matplotlib.interactive(True)
209
210 @classmethod
211 def get_kernel(cls):
212 """ Return the global kernel instance or raise a RuntimeError if it does
213 not exist.
214 """
215 if cls._kernel is None:
216 raise RuntimeError("Kernel not started!")
217 else:
218 return cls._kernel
219
220 def start(self):
221 """ Start the kernel main loop.
222 """
223 # Set the global kernel instance.
224 Kernel._kernel = self
225
149 while True:
226 while True:
150 try:
227 ident = self.reply_socket.recv()
151 ident = self.reply_socket.recv(zmq.NOBLOCK)
228 assert self.reply_socket.rcvmore(), "Missing message part."
152 except zmq.ZMQError, e:
229 msg = self.reply_socket.recv_json()
153 if e.errno == zmq.EAGAIN:
230 omsg = Message(msg)
154 break
231 print>>sys.__stdout__
232 print>>sys.__stdout__, omsg
233 handler = self.handlers.get(omsg.msg_type, None)
234 if handler is None:
235 print >> sys.__stderr__, "UNKNOWN MESSAGE TYPE:", omsg
155 else:
236 else:
156 assert self.reply_socket.rcvmore(), "Unexpected missing message part."
237 handler(ident, omsg)
157 msg = self.reply_socket.recv_json()
238
158 print>>sys.__stdout__, "Aborting:"
239 #---------------------------------------------------------------------------
159 print>>sys.__stdout__, Message(msg)
240 # Kernel request handlers
160 msg_type = msg['msg_type']
241 #---------------------------------------------------------------------------
161 reply_type = msg_type.split('_')[0] + '_reply'
162 reply_msg = self.session.msg(reply_type, {'status' : 'aborted'}, msg)
163 print>>sys.__stdout__, Message(reply_msg)
164 self.reply_socket.send(ident,zmq.SNDMORE)
165 self.reply_socket.send_json(reply_msg)
166 # We need to wait a bit for requests to come in. This can probably
167 # be set shorter for true asynchronous clients.
168 time.sleep(0.1)
169
242
170 def execute_request(self, ident, parent):
243 def execute_request(self, ident, parent):
171 try:
244 try:
@@ -177,12 +250,15 b' class Kernel(object):'
177 pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
250 pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
178 self.pub_socket.send_json(pyin_msg)
251 self.pub_socket.send_json(pyin_msg)
179
252
253 # Clear the execute payload from the last request.
254 self._exec_payload = {}
255
180 try:
256 try:
181 comp_code = self.compiler(code, '<zmq-kernel>')
257 comp_code = self.compiler(code, '<zmq-kernel>')
182
258
183 # Replace raw_input. Note that is not sufficient to replace
259 # Replace raw_input. Note that is not sufficient to replace
184 # raw_input in the user namespace.
260 # raw_input in the user namespace.
185 raw_input = lambda prompt='': self.raw_input(prompt, ident, parent)
261 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
186 __builtin__.raw_input = raw_input
262 __builtin__.raw_input = raw_input
187
263
188 # Configure the display hook.
264 # Configure the display hook.
@@ -203,7 +279,7 b' class Kernel(object):'
203 self.pub_socket.send_json(exc_msg)
279 self.pub_socket.send_json(exc_msg)
204 reply_content = exc_content
280 reply_content = exc_content
205 else:
281 else:
206 reply_content = {'status' : 'ok'}
282 reply_content = { 'status' : 'ok', 'payload' : self._exec_payload }
207
283
208 # Flush output before sending the reply.
284 # Flush output before sending the reply.
209 sys.stderr.flush()
285 sys.stderr.flush()
@@ -215,9 +291,49 b' class Kernel(object):'
215 self.reply_socket.send(ident, zmq.SNDMORE)
291 self.reply_socket.send(ident, zmq.SNDMORE)
216 self.reply_socket.send_json(reply_msg)
292 self.reply_socket.send_json(reply_msg)
217 if reply_msg['content']['status'] == u'error':
293 if reply_msg['content']['status'] == u'error':
218 self.abort_queue()
294 self._abort_queue()
295
296 def complete_request(self, ident, parent):
297 comp = self.completer.complete(parent.content.line, parent.content.text)
298 matches = {'matches' : comp, 'status' : 'ok'}
299 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
300 matches, parent, ident)
301 print >> sys.__stdout__, completion_msg
302
303 def object_info_request(self, ident, parent):
304 context = parent['content']['oname'].split('.')
305 object_info = self._object_info(context)
306 msg = self.session.send(self.reply_socket, 'object_info_reply',
307 object_info, parent, ident)
308 print >> sys.__stdout__, msg
309
310 #---------------------------------------------------------------------------
311 # Protected interface
312 #---------------------------------------------------------------------------
313
314 def _abort_queue(self):
315 while True:
316 try:
317 ident = self.reply_socket.recv(zmq.NOBLOCK)
318 except zmq.ZMQError, e:
319 if e.errno == zmq.EAGAIN:
320 break
321 else:
322 assert self.reply_socket.rcvmore(), "Missing message part."
323 msg = self.reply_socket.recv_json()
324 print>>sys.__stdout__, "Aborting:"
325 print>>sys.__stdout__, Message(msg)
326 msg_type = msg['msg_type']
327 reply_type = msg_type.split('_')[0] + '_reply'
328 reply_msg = self.session.msg(reply_type, {'status':'aborted'}, msg)
329 print>>sys.__stdout__, Message(reply_msg)
330 self.reply_socket.send(ident,zmq.SNDMORE)
331 self.reply_socket.send_json(reply_msg)
332 # We need to wait a bit for requests to come in. This can probably
333 # be set shorter for true asynchronous clients.
334 time.sleep(0.1)
219
335
220 def raw_input(self, prompt, ident, parent):
336 def _raw_input(self, prompt, ident, parent):
221 # Flush output before making the request.
337 # Flush output before making the request.
222 sys.stderr.flush()
338 sys.stderr.flush()
223 sys.stdout.flush()
339 sys.stdout.flush()
@@ -237,25 +353,8 b' class Kernel(object):'
237 value = ''
353 value = ''
238 return value
354 return value
239
355
240 def complete_request(self, ident, parent):
356 def _object_info(self, context):
241 matches = {'matches' : self.complete(parent),
357 symbol, leftover = self._symbol_from_context(context)
242 'status' : 'ok'}
243 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
244 matches, parent, ident)
245 print >> sys.__stdout__, completion_msg
246
247 def complete(self, msg):
248 return self.completer.complete(msg.content.line, msg.content.text)
249
250 def object_info_request(self, ident, parent):
251 context = parent['content']['oname'].split('.')
252 object_info = self.object_info(context)
253 msg = self.session.send(self.reply_socket, 'object_info_reply',
254 object_info, parent, ident)
255 print >> sys.__stdout__, msg
256
257 def object_info(self, context):
258 symbol, leftover = self.symbol_from_context(context)
259 if symbol is not None and not leftover:
358 if symbol is not None and not leftover:
260 doc = getattr(symbol, '__doc__', '')
359 doc = getattr(symbol, '__doc__', '')
261 else:
360 else:
@@ -263,7 +362,7 b' class Kernel(object):'
263 object_info = dict(docstring = doc)
362 object_info = dict(docstring = doc)
264 return object_info
363 return object_info
265
364
266 def symbol_from_context(self, context):
365 def _symbol_from_context(self, context):
267 if not context:
366 if not context:
268 return None, context
367 return None, context
269
368
@@ -284,20 +383,6 b' class Kernel(object):'
284
383
285 return symbol, []
384 return symbol, []
286
385
287 def start(self):
288 while True:
289 ident = self.reply_socket.recv()
290 assert self.reply_socket.rcvmore(), "Missing message part."
291 msg = self.reply_socket.recv_json()
292 omsg = Message(msg)
293 print>>sys.__stdout__
294 print>>sys.__stdout__, omsg
295 handler = self.handlers.get(omsg.msg_type, None)
296 if handler is None:
297 print >> sys.__stderr__, "UNKNOWN MESSAGE TYPE:", omsg
298 else:
299 handler(ident, omsg)
300
301 #-----------------------------------------------------------------------------
386 #-----------------------------------------------------------------------------
302 # Kernel main and launch functions
387 # Kernel main and launch functions
303 #-----------------------------------------------------------------------------
388 #-----------------------------------------------------------------------------
@@ -342,8 +427,8 b' class ExitPollerWindows(Thread):'
342
427
343
428
344 def bind_port(socket, ip, port):
429 def bind_port(socket, ip, port):
345 """ Binds the specified ZMQ socket. If the port is less than zero, a random
430 """ Binds the specified ZMQ socket. If the port is zero, a random port is
346 port is chosen. Returns the port that was bound.
431 chosen. Returns the port that was bound.
347 """
432 """
348 connection = 'tcp://%s' % ip
433 connection = 'tcp://%s' % ip
349 if port <= 0:
434 if port <= 0:
@@ -374,6 +459,12 b' def main():'
374 else:
459 else:
375 parser.add_argument('--parent', action='store_true',
460 parser.add_argument('--parent', action='store_true',
376 help='kill this process if its parent dies')
461 help='kill this process if its parent dies')
462 parser.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
463 const='auto', help = \
464 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
465 given, the GUI backend is matplotlib's, otherwise use one of: \
466 ['tk', 'gtk', 'qt', 'wx', 'payload-svg'].")
467
377 namespace = parser.parse_args()
468 namespace = parser.parse_args()
378
469
379 # Create a context, a session, and the kernel sockets.
470 # Create a context, a session, and the kernel sockets.
@@ -393,14 +484,21 b' def main():'
393 req_port = bind_port(req_socket, namespace.ip, namespace.req)
484 req_port = bind_port(req_socket, namespace.ip, namespace.req)
394 print >>sys.__stdout__, "REQ Channel on port", req_port
485 print >>sys.__stdout__, "REQ Channel on port", req_port
395
486
487 # Create the kernel.
488 kernel = Kernel(session, reply_socket, pub_socket, req_socket)
489
490 # Set up pylab, if necessary.
491 if namespace.pylab:
492 if namespace.pylab == 'auto':
493 kernel.activate_pylab()
494 else:
495 kernel.activate_pylab(namespace.pylab)
496
396 # Redirect input streams and set a display hook.
497 # Redirect input streams and set a display hook.
397 sys.stdout = OutStream(session, pub_socket, u'stdout')
498 sys.stdout = OutStream(session, pub_socket, u'stdout')
398 sys.stderr = OutStream(session, pub_socket, u'stderr')
499 sys.stderr = OutStream(session, pub_socket, u'stderr')
399 sys.displayhook = DisplayHook(session, pub_socket)
500 sys.displayhook = DisplayHook(session, pub_socket)
400
501
401 # Create the kernel.
402 kernel = Kernel(session, reply_socket, pub_socket, req_socket)
403
404 # Configure this kernel/process to die on parent termination, if necessary.
502 # Configure this kernel/process to die on parent termination, if necessary.
405 if namespace.parent:
503 if namespace.parent:
406 if sys.platform == 'win32':
504 if sys.platform == 'win32':
General Comments 0
You need to be logged in to leave comments. Login now