##// END OF EJS Templates
* Restored functionality after major merge....
epatters -
Show More
@@ -0,0 +1,189 b''
1 """ Defines helper functions for creating kernel entry points and process
2 launchers.
3 """
4
5 # Standard library imports.
6 import socket
7 from subprocess import Popen
8 import sys
9
10 # System library imports.
11 import zmq
12
13 # Local imports.
14 from IPython.external.argparse import ArgumentParser
15 from exitpoller import ExitPollerUnix, ExitPollerWindows
16 from displayhook import DisplayHook
17 from iostream import OutStream
18 from session import Session
19
20
21 def bind_port(socket, ip, port):
22 """ Binds the specified ZMQ socket. If the port is zero, a random port is
23 chosen. Returns the port that was bound.
24 """
25 connection = 'tcp://%s' % ip
26 if port <= 0:
27 port = socket.bind_to_random_port(connection)
28 else:
29 connection += ':%i' % port
30 socket.bind(connection)
31 return port
32
33
34 def make_argument_parser():
35 """ Creates an ArgumentParser for the generic arguments supported by all
36 kernel entry points.
37 """
38 parser = ArgumentParser()
39 parser.add_argument('--ip', type=str, default='127.0.0.1',
40 help='set the kernel\'s IP address [default: local]')
41 parser.add_argument('--xrep', type=int, metavar='PORT', default=0,
42 help='set the XREP channel port [default: random]')
43 parser.add_argument('--pub', type=int, metavar='PORT', default=0,
44 help='set the PUB channel port [default: random]')
45 parser.add_argument('--req', type=int, metavar='PORT', default=0,
46 help='set the REQ channel port [default: random]')
47
48 if sys.platform == 'win32':
49 parser.add_argument('--parent', type=int, metavar='HANDLE',
50 default=0, help='kill this process if the process '
51 'with HANDLE dies')
52 else:
53 parser.add_argument('--parent', action='store_true',
54 help='kill this process if its parent dies')
55
56 return parser
57
58
59 def make_kernel(namespace, kernel_factory, out_stream_factory=OutStream,
60 display_hook_factory=DisplayHook):
61 """ Creates a kernel.
62 """
63 # Create a context, a session, and the kernel sockets.
64 print >>sys.__stdout__, "Starting the kernel..."
65 context = zmq.Context()
66 session = Session(username=u'kernel')
67
68 reply_socket = context.socket(zmq.XREP)
69 xrep_port = bind_port(reply_socket, namespace.ip, namespace.xrep)
70 print >>sys.__stdout__, "XREP Channel on port", xrep_port
71
72 pub_socket = context.socket(zmq.PUB)
73 pub_port = bind_port(pub_socket, namespace.ip, namespace.pub)
74 print >>sys.__stdout__, "PUB Channel on port", pub_port
75
76 req_socket = context.socket(zmq.XREQ)
77 req_port = bind_port(req_socket, namespace.ip, namespace.req)
78 print >>sys.__stdout__, "REQ Channel on port", req_port
79
80 # Redirect input streams and set a display hook.
81 sys.stdout = out_stream_factory(session, pub_socket, u'stdout')
82 sys.stderr = out_stream_factory(session, pub_socket, u'stderr')
83 sys.displayhook = display_hook_factory(session, pub_socket)
84
85 # Create the kernel.
86 return kernel_factory(session=session, reply_socket=reply_socket,
87 pub_socket=pub_socket, req_socket=req_socket)
88
89
90 def start_kernel(namespace, kernel):
91 """ Starts a kernel.
92 """
93 # Configure this kernel/process to die on parent termination, if necessary.
94 if namespace.parent:
95 if sys.platform == 'win32':
96 poller = ExitPollerWindows(namespace.parent)
97 else:
98 poller = ExitPollerUnix()
99 poller.start()
100
101 # Start the kernel mainloop.
102 kernel.start()
103
104
105 def make_default_main(kernel_factory):
106 """ Creates the simplest possible kernel entry point.
107 """
108 def main():
109 namespace = make_argument_parser().parse_args()
110 kernel = make_kernel(namespace, kernel_factory)
111 start_kernel(namespace, kernel)
112 return main
113
114
115 def base_launch_kernel(code, xrep_port=0, pub_port=0, req_port=0,
116 independent=False, extra_arguments=[]):
117 """ Launches a localhost kernel, binding to the specified ports.
118
119 Parameters
120 ----------
121 code : str,
122 A string of Python code that imports and executes a kernel entry point.
123
124 xrep_port : int, optional
125 The port to use for XREP channel.
126
127 pub_port : int, optional
128 The port to use for the SUB channel.
129
130 req_port : int, optional
131 The port to use for the REQ (raw input) channel.
132
133 independent : bool, optional (default False)
134 If set, the kernel process is guaranteed to survive if this process
135 dies. If not set, an effort is made to ensure that the kernel is killed
136 when this process dies. Note that in this case it is still good practice
137 to kill kernels manually before exiting.
138
139 extra_arguments = list, optional
140 A list of extra arguments to pass when executing the launch code.
141
142 Returns
143 -------
144 A tuple of form:
145 (kernel_process, xrep_port, pub_port, req_port)
146 where kernel_process is a Popen object and the ports are integers.
147 """
148 # Find open ports as necessary.
149 ports = []
150 ports_needed = int(xrep_port <= 0) + int(pub_port <= 0) + int(req_port <= 0)
151 for i in xrange(ports_needed):
152 sock = socket.socket()
153 sock.bind(('', 0))
154 ports.append(sock)
155 for i, sock in enumerate(ports):
156 port = sock.getsockname()[1]
157 sock.close()
158 ports[i] = port
159 if xrep_port <= 0:
160 xrep_port = ports.pop(0)
161 if pub_port <= 0:
162 pub_port = ports.pop(0)
163 if req_port <= 0:
164 req_port = ports.pop(0)
165
166 # Build the kernel launch command.
167 arguments = [ sys.executable, '-c', code, '--xrep', str(xrep_port),
168 '--pub', str(pub_port), '--req', str(req_port) ]
169 arguments.extend(extra_arguments)
170
171 # Spawn a kernel.
172 if independent:
173 if sys.platform == 'win32':
174 proc = Popen(['start', '/b'] + arguments, shell=True)
175 else:
176 proc = Popen(arguments, preexec_fn=lambda: os.setsid())
177 else:
178 if sys.platform == 'win32':
179 from _subprocess import DuplicateHandle, GetCurrentProcess, \
180 DUPLICATE_SAME_ACCESS
181 pid = GetCurrentProcess()
182 handle = DuplicateHandle(pid, pid, pid, 0,
183 True, # Inheritable by new processes.
184 DUPLICATE_SAME_ACCESS)
185 proc = Popen(arguments + ['--parent', str(int(handle))])
186 else:
187 proc = Popen(arguments + ['--parent'])
188
189 return proc, xrep_port, pub_port, req_port
@@ -1,53 +1,63 b''
1 """ A demo of the Qt console-style IPython frontend.
1 """ A demo of the Qt console-style IPython frontend.
2 """
2 """
3
3
4 # Systemm library imports
4 # Systemm library imports
5 from PyQt4 import QtCore, QtGui
5 from PyQt4 import QtCore, QtGui
6
6
7 # Local imports
7 # Local imports
8 from IPython.external.argparse import ArgumentParser
8 from IPython.external.argparse import ArgumentParser
9 from IPython.frontend.qt.kernelmanager import QtKernelManager
9 from IPython.frontend.qt.kernelmanager import QtKernelManager
10 from ipython_widget import IPythonWidget
11 from rich_ipython_widget import RichIPythonWidget
12
10
13
11
14 def main():
12 def main():
15 """ Entry point for demo.
13 """ Entry point for demo.
16 """
14 """
17 # Parse command line arguments.
15 # Parse command line arguments.
18 parser = ArgumentParser()
16 parser = ArgumentParser()
19 parser.add_argument('--pylab', action='store_true',
17 group = parser.add_mutually_exclusive_group()
20 help='start kernel with pylab enabled')
18 group.add_argument('--pure', action='store_true', help = \
19 'use a pure Python kernel instead of an IPython kernel')
20 group.add_argument('--pylab', action='store_true',
21 help='use a kernel with PyLab enabled')
21 parser.add_argument('--rich', action='store_true',
22 parser.add_argument('--rich', action='store_true',
22 help='use rich text frontend')
23 help='use a rich text frontend')
23 namespace = parser.parse_args()
24 namespace = parser.parse_args()
24
25
25 # Don't let Qt or ZMQ swallow KeyboardInterupts.
26 # Don't let Qt or ZMQ swallow KeyboardInterupts.
26 import signal
27 import signal
27 signal.signal(signal.SIGINT, signal.SIG_DFL)
28 signal.signal(signal.SIGINT, signal.SIG_DFL)
28
29
29 # Create a KernelManager and start a kernel.
30 # Create a KernelManager and start a kernel.
30 kernel_manager = QtKernelManager()
31 kernel_manager = QtKernelManager()
31 if namespace.pylab:
32 if namespace.pure:
33 kernel_manager.start_kernel(ipython=False)
34 elif namespace.pylab:
32 if namespace.rich:
35 if namespace.rich:
33 kernel_manager.start_kernel(pylab='payload-svg')
36 kernel_manager.start_kernel(pylab='payload-svg')
34 else:
37 else:
35 kernel_manager.start_kernel(pylab='qt4')
38 kernel_manager.start_kernel(pylab='qt4')
36 else:
39 else:
37 kernel_manager.start_kernel()
40 kernel_manager.start_kernel()
38 kernel_manager.start_channels()
41 kernel_manager.start_channels()
39
42
40 # Launch the application.
43 # Launch the application.
41 app = QtGui.QApplication([])
44 app = QtGui.QApplication([])
42 if namespace.rich:
45 if namespace.pure:
43 widget = RichIPythonWidget()
46 from frontend_widget import FrontendWidget
47 kind = 'rich' if namespace.rich else 'plain'
48 widget = FrontendWidget(kind=kind)
44 else:
49 else:
45 widget = IPythonWidget()
50 if namespace.rich:
51 from rich_ipython_widget import RichIPythonWidget
52 widget = RichIPythonWidget()
53 else:
54 from ipython_widget import IPythonWidget
55 widget = IPythonWidget()
46 widget.kernel_manager = kernel_manager
56 widget.kernel_manager = kernel_manager
47 widget.setWindowTitle('Python')
57 widget.setWindowTitle('Python')
48 widget.show()
58 widget.show()
49 app.exec_()
59 app.exec_()
50
60
51
61
52 if __name__ == '__main__':
62 if __name__ == '__main__':
53 main()
63 main()
@@ -1,367 +1,367 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """A simple interactive kernel that talks to a frontend over 0MQ.
2 """A simple interactive kernel that talks to a frontend over 0MQ.
3
3
4 Things to do:
4 Things to do:
5
5
6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
7 call set_parent on all the PUB objects with the message about to be executed.
7 call set_parent on all the PUB objects with the message about to be executed.
8 * Implement random port and security key logic.
8 * Implement random port and security key logic.
9 * Implement control messages.
9 * Implement control messages.
10 * Implement event loop and poll version.
10 * Implement event loop and poll version.
11 """
11 """
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 # Standard library imports.
17 # Standard library imports.
18 import __builtin__
18 import __builtin__
19 from code import CommandCompiler
19 from code import CommandCompiler
20 import os
21 import sys
20 import sys
22 import time
21 import time
23 import traceback
22 import traceback
24
23
25 # System library imports.
24 # System library imports.
26 import zmq
25 import zmq
27
26
28 # Local imports.
27 # Local imports.
29 from IPython.config.configurable import Configurable
28 from IPython.config.configurable import Configurable
30 from IPython.zmq.zmqshell import ZMQInteractiveShell
31 from IPython.external.argparse import ArgumentParser
32 from IPython.utils.traitlets import Instance
29 from IPython.utils.traitlets import Instance
33 from IPython.zmq.session import Session, Message
34 from completer import KernelCompleter
30 from completer import KernelCompleter
35 from iostream import OutStream
31 from entry_point import base_launch_kernel, make_argument_parser, make_kernel, \
36 from displayhook import DisplayHook
32 start_kernel
37 from exitpoller import ExitPollerUnix, ExitPollerWindows
33 from session import Session, Message
34 from zmqshell import ZMQInteractiveShell
38
35
39 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
40 # Main kernel class
37 # Main kernel class
41 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
42
39
43 class Kernel(Configurable):
40 class Kernel(Configurable):
44
41
42 #---------------------------------------------------------------------------
43 # Kernel interface
44 #---------------------------------------------------------------------------
45
45 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
46 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
46 session = Instance('IPython.zmq.session.Session')
47 session = Instance(Session)
47 reply_socket = Instance('zmq.Socket')
48 reply_socket = Instance('zmq.Socket')
48 pub_socket = Instance('zmq.Socket')
49 pub_socket = Instance('zmq.Socket')
49 req_socket = Instance('zmq.Socket')
50 req_socket = Instance('zmq.Socket')
50
51
52 # The global kernel instance.
53 _kernel = None
54
55 # Maps user-friendly backend names to matplotlib backend identifiers.
56 _pylab_map = { 'tk': 'TkAgg',
57 'gtk': 'GTKAgg',
58 'wx': 'WXAgg',
59 'qt': 'Qt4Agg', # qt3 not supported
60 'qt4': 'Qt4Agg',
61 'payload-svg' : \
62 'module://IPython.zmq.pylab.backend_payload_svg' }
63
51 def __init__(self, **kwargs):
64 def __init__(self, **kwargs):
52 super(Kernel, self).__init__(**kwargs)
65 super(Kernel, self).__init__(**kwargs)
53 self.shell = ZMQInteractiveShell.instance()
66 self.shell = ZMQInteractiveShell.instance()
67
68 # Protected variables.
69 self._exec_payload = {}
54
70
55 # Build dict of handlers for message types
71 # Build dict of handlers for message types
56 msg_types = [ 'execute_request', 'complete_request',
72 msg_types = [ 'execute_request', 'complete_request',
57 'object_info_request' ]
73 'object_info_request' ]
58 self.handlers = {}
74 self.handlers = {}
59 for msg_type in msg_types:
75 for msg_type in msg_types:
60 self.handlers[msg_type] = getattr(self, msg_type)
76 self.handlers[msg_type] = getattr(self, msg_type)
61
77
62 def abort_queue(self):
78 def add_exec_payload(self, key, value):
79 """ Adds a key/value pair to the execute payload.
80 """
81 self._exec_payload[key] = value
82
83 def activate_pylab(self, backend=None, import_all=True):
84 """ Activates pylab in this kernel's namespace.
85
86 Parameters:
87 -----------
88 backend : str, optional
89 A valid backend name.
90
91 import_all : bool, optional
92 If true, an 'import *' is done from numpy and pylab.
93 """
94 # FIXME: This is adapted from IPython.lib.pylabtools.pylab_activate.
95 # Common funtionality should be refactored.
96
97 # We must set the desired backend before importing pylab.
98 import matplotlib
99 if backend:
100 backend_id = self._pylab_map[backend]
101 if backend_id.startswith('module://'):
102 # Work around bug in matplotlib: matplotlib.use converts the
103 # backend_id to lowercase even if a module name is specified!
104 matplotlib.rcParams['backend'] = backend_id
105 else:
106 matplotlib.use(backend_id)
107
108 # Import numpy as np/pyplot as plt are conventions we're trying to
109 # somewhat standardize on. Making them available to users by default
110 # will greatly help this.
111 exec ("import numpy\n"
112 "import matplotlib\n"
113 "from matplotlib import pylab, mlab, pyplot\n"
114 "np = numpy\n"
115 "plt = pyplot\n"
116 ) in self.shell.user_ns
117
118 if import_all:
119 exec("from matplotlib.pylab import *\n"
120 "from numpy import *\n") in self.shell.user_ns
121
122 matplotlib.interactive(True)
123
124 @classmethod
125 def get_kernel(cls):
126 """ Return the global kernel instance or raise a RuntimeError if it does
127 not exist.
128 """
129 if cls._kernel is None:
130 raise RuntimeError("Kernel not started!")
131 else:
132 return cls._kernel
133
134 def start(self):
135 """ Start the kernel main loop.
136 """
137 # Set the global kernel instance.
138 self.__class__._kernel = self
139
63 while True:
140 while True:
64 try:
141 ident = self.reply_socket.recv()
65 ident = self.reply_socket.recv(zmq.NOBLOCK)
142 assert self.reply_socket.rcvmore(), "Missing message part."
66 except zmq.ZMQError, e:
143 msg = self.reply_socket.recv_json()
67 if e.errno == zmq.EAGAIN:
144 omsg = Message(msg)
68 break
145 print>>sys.__stdout__
146 print>>sys.__stdout__, omsg
147 handler = self.handlers.get(omsg.msg_type, None)
148 if handler is None:
149 print >> sys.__stderr__, "UNKNOWN MESSAGE TYPE:", omsg
69 else:
150 else:
70 assert self.reply_socket.rcvmore(), "Unexpected missing message part."
151 handler(ident, omsg)
71 msg = self.reply_socket.recv_json()
152
72 print>>sys.__stdout__, "Aborting:"
153 #---------------------------------------------------------------------------
73 print>>sys.__stdout__, Message(msg)
154 # Kernel request handlers
74 msg_type = msg['msg_type']
155 #---------------------------------------------------------------------------
75 reply_type = msg_type.split('_')[0] + '_reply'
76 reply_msg = self.session.msg(reply_type, {'status' : 'aborted'}, msg)
77 print>>sys.__stdout__, Message(reply_msg)
78 self.reply_socket.send(ident,zmq.SNDMORE)
79 self.reply_socket.send_json(reply_msg)
80 # We need to wait a bit for requests to come in. This can probably
81 # be set shorter for true asynchronous clients.
82 time.sleep(0.1)
83
156
84 def execute_request(self, ident, parent):
157 def execute_request(self, ident, parent):
85 try:
158 try:
86 code = parent[u'content'][u'code']
159 code = parent[u'content'][u'code']
87 except:
160 except:
88 print>>sys.__stderr__, "Got bad msg: "
161 print>>sys.__stderr__, "Got bad msg: "
89 print>>sys.__stderr__, Message(parent)
162 print>>sys.__stderr__, Message(parent)
90 return
163 return
91 pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
164 pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
92 self.pub_socket.send_json(pyin_msg)
165 self.pub_socket.send_json(pyin_msg)
93
166
167 # Clear the execute payload from the last request.
168 self._exec_payload = {}
169
94 try:
170 try:
95 # Replace raw_input. Note that is not sufficient to replace
171 # Replace raw_input. Note that is not sufficient to replace
96 # raw_input in the user namespace.
172 # raw_input in the user namespace.
97 raw_input = lambda prompt='': self.raw_input(prompt, ident, parent)
173 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
98 __builtin__.raw_input = raw_input
174 __builtin__.raw_input = raw_input
99
175
100 # Configure the display hook.
176 # Configure the display hook.
101 sys.displayhook.set_parent(parent)
177 sys.displayhook.set_parent(parent)
102
178
103 self.shell.runlines(code)
179 self.shell.runlines(code)
104 # exec comp_code in self.user_ns, self.user_ns
180 # exec comp_code in self.user_ns, self.user_ns
105 except:
181 except:
106 etype, evalue, tb = sys.exc_info()
182 etype, evalue, tb = sys.exc_info()
107 tb = traceback.format_exception(etype, evalue, tb)
183 tb = traceback.format_exception(etype, evalue, tb)
108 exc_content = {
184 exc_content = {
109 u'status' : u'error',
185 u'status' : u'error',
110 u'traceback' : tb,
186 u'traceback' : tb,
111 u'ename' : unicode(etype.__name__),
187 u'ename' : unicode(etype.__name__),
112 u'evalue' : unicode(evalue)
188 u'evalue' : unicode(evalue)
113 }
189 }
114 exc_msg = self.session.msg(u'pyerr', exc_content, parent)
190 exc_msg = self.session.msg(u'pyerr', exc_content, parent)
115 self.pub_socket.send_json(exc_msg)
191 self.pub_socket.send_json(exc_msg)
116 reply_content = exc_content
192 reply_content = exc_content
117 else:
193 else:
118 reply_content = {'status' : 'ok'}
194 reply_content = { 'status' : 'ok', 'payload' : self._exec_payload }
119
195
120 # Flush output before sending the reply.
196 # Flush output before sending the reply.
121 sys.stderr.flush()
197 sys.stderr.flush()
122 sys.stdout.flush()
198 sys.stdout.flush()
123
199
124 # Send the reply.
200 # Send the reply.
125 reply_msg = self.session.msg(u'execute_reply', reply_content, parent)
201 reply_msg = self.session.msg(u'execute_reply', reply_content, parent)
126 print>>sys.__stdout__, Message(reply_msg)
202 print>>sys.__stdout__, Message(reply_msg)
127 self.reply_socket.send(ident, zmq.SNDMORE)
203 self.reply_socket.send(ident, zmq.SNDMORE)
128 self.reply_socket.send_json(reply_msg)
204 self.reply_socket.send_json(reply_msg)
129 if reply_msg['content']['status'] == u'error':
205 if reply_msg['content']['status'] == u'error':
130 self.abort_queue()
206 self._abort_queue()
207
208 def complete_request(self, ident, parent):
209 matches = {'matches' : self._complete(parent),
210 'status' : 'ok'}
211 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
212 matches, parent, ident)
213 print >> sys.__stdout__, completion_msg
214
215 def object_info_request(self, ident, parent):
216 context = parent['content']['oname'].split('.')
217 object_info = self._object_info(context)
218 msg = self.session.send(self.reply_socket, 'object_info_reply',
219 object_info, parent, ident)
220 print >> sys.__stdout__, msg
221
222 #---------------------------------------------------------------------------
223 # Protected interface
224 #---------------------------------------------------------------------------
131
225
132 def raw_input(self, prompt, ident, parent):
226 def _abort_queue(self):
227 while True:
228 try:
229 ident = self.reply_socket.recv(zmq.NOBLOCK)
230 except zmq.ZMQError, e:
231 if e.errno == zmq.EAGAIN:
232 break
233 else:
234 assert self.reply_socket.rcvmore(), "Unexpected missing message part."
235 msg = self.reply_socket.recv_json()
236 print>>sys.__stdout__, "Aborting:"
237 print>>sys.__stdout__, Message(msg)
238 msg_type = msg['msg_type']
239 reply_type = msg_type.split('_')[0] + '_reply'
240 reply_msg = self.session.msg(reply_type, {'status' : 'aborted'}, msg)
241 print>>sys.__stdout__, Message(reply_msg)
242 self.reply_socket.send(ident,zmq.SNDMORE)
243 self.reply_socket.send_json(reply_msg)
244 # We need to wait a bit for requests to come in. This can probably
245 # be set shorter for true asynchronous clients.
246 time.sleep(0.1)
247
248 def _raw_input(self, prompt, ident, parent):
133 # Flush output before making the request.
249 # Flush output before making the request.
134 sys.stderr.flush()
250 sys.stderr.flush()
135 sys.stdout.flush()
251 sys.stdout.flush()
136
252
137 # Send the input request.
253 # Send the input request.
138 content = dict(prompt=prompt)
254 content = dict(prompt=prompt)
139 msg = self.session.msg(u'input_request', content, parent)
255 msg = self.session.msg(u'input_request', content, parent)
140 self.req_socket.send_json(msg)
256 self.req_socket.send_json(msg)
141
257
142 # Await a response.
258 # Await a response.
143 reply = self.req_socket.recv_json()
259 reply = self.req_socket.recv_json()
144 try:
260 try:
145 value = reply['content']['value']
261 value = reply['content']['value']
146 except:
262 except:
147 print>>sys.__stderr__, "Got bad raw_input reply: "
263 print>>sys.__stderr__, "Got bad raw_input reply: "
148 print>>sys.__stderr__, Message(parent)
264 print>>sys.__stderr__, Message(parent)
149 value = ''
265 value = ''
150 return value
266 return value
151
267
152 def complete_request(self, ident, parent):
268 def _complete(self, msg):
153 matches = {'matches' : self.complete(parent),
154 'status' : 'ok'}
155 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
156 matches, parent, ident)
157 print >> sys.__stdout__, completion_msg
158
159 def complete(self, msg):
160 return self.shell.complete(msg.content.line)
269 return self.shell.complete(msg.content.line)
161
270
162 def object_info_request(self, ident, parent):
271 def _object_info(self, context):
163 context = parent['content']['oname'].split('.')
272 symbol, leftover = self._symbol_from_context(context)
164 object_info = self.object_info(context)
165 msg = self.session.send(self.reply_socket, 'object_info_reply',
166 object_info, parent, ident)
167 print >> sys.__stdout__, msg
168
169 def object_info(self, context):
170 symbol, leftover = self.symbol_from_context(context)
171 if symbol is not None and not leftover:
273 if symbol is not None and not leftover:
172 doc = getattr(symbol, '__doc__', '')
274 doc = getattr(symbol, '__doc__', '')
173 else:
275 else:
174 doc = ''
276 doc = ''
175 object_info = dict(docstring = doc)
277 object_info = dict(docstring = doc)
176 return object_info
278 return object_info
177
279
178 def symbol_from_context(self, context):
280 def _symbol_from_context(self, context):
179 if not context:
281 if not context:
180 return None, context
282 return None, context
181
283
182 base_symbol_string = context[0]
284 base_symbol_string = context[0]
183 symbol = self.shell.user_ns.get(base_symbol_string, None)
285 symbol = self.shell.user_ns.get(base_symbol_string, None)
184 if symbol is None:
286 if symbol is None:
185 symbol = __builtin__.__dict__.get(base_symbol_string, None)
287 symbol = __builtin__.__dict__.get(base_symbol_string, None)
186 if symbol is None:
288 if symbol is None:
187 return None, context
289 return None, context
188
290
189 context = context[1:]
291 context = context[1:]
190 for i, name in enumerate(context):
292 for i, name in enumerate(context):
191 new_symbol = getattr(symbol, name, None)
293 new_symbol = getattr(symbol, name, None)
192 if new_symbol is None:
294 if new_symbol is None:
193 return symbol, context[i:]
295 return symbol, context[i:]
194 else:
296 else:
195 symbol = new_symbol
297 symbol = new_symbol
196
298
197 return symbol, []
299 return symbol, []
198
300
199 def start(self):
200 while True:
201 ident = self.reply_socket.recv()
202 assert self.reply_socket.rcvmore(), "Missing message part."
203 msg = self.reply_socket.recv_json()
204 omsg = Message(msg)
205 print>>sys.__stdout__
206 print>>sys.__stdout__, omsg
207 handler = self.handlers.get(omsg.msg_type, None)
208 if handler is None:
209 print >> sys.__stderr__, "UNKNOWN MESSAGE TYPE:", omsg
210 else:
211 handler(ident, omsg)
212
213 #-----------------------------------------------------------------------------
301 #-----------------------------------------------------------------------------
214 # Kernel main and launch functions
302 # Kernel main and launch functions
215 #-----------------------------------------------------------------------------
303 #-----------------------------------------------------------------------------
216
304
217 def bind_port(socket, ip, port):
305 def launch_kernel(xrep_port=0, pub_port=0, req_port=0, independent=False,
218 """ Binds the specified ZMQ socket. If the port is less than zero, a random
306 pylab=False):
219 port is chosen. Returns the port that was bound.
220 """
221 connection = 'tcp://%s' % ip
222 if port <= 0:
223 port = socket.bind_to_random_port(connection)
224 else:
225 connection += ':%i' % port
226 socket.bind(connection)
227 return port
228
229
230 def main():
231 """ Main entry point for launching a kernel.
232 """
233 # Parse command line arguments.
234 parser = ArgumentParser()
235 parser.add_argument('--ip', type=str, default='127.0.0.1',
236 help='set the kernel\'s IP address [default: local]')
237 parser.add_argument('--xrep', type=int, metavar='PORT', default=0,
238 help='set the XREP channel port [default: random]')
239 parser.add_argument('--pub', type=int, metavar='PORT', default=0,
240 help='set the PUB channel port [default: random]')
241 parser.add_argument('--req', type=int, metavar='PORT', default=0,
242 help='set the REQ channel port [default: random]')
243 if sys.platform == 'win32':
244 parser.add_argument('--parent', type=int, metavar='HANDLE',
245 default=0, help='kill this process if the process '
246 'with HANDLE dies')
247 else:
248 parser.add_argument('--parent', action='store_true',
249 help='kill this process if its parent dies')
250 namespace = parser.parse_args()
251
252 # Create a context, a session, and the kernel sockets.
253 print >>sys.__stdout__, "Starting the kernel..."
254 context = zmq.Context()
255 session = Session(username=u'kernel')
256
257 reply_socket = context.socket(zmq.XREP)
258 xrep_port = bind_port(reply_socket, namespace.ip, namespace.xrep)
259 print >>sys.__stdout__, "XREP Channel on port", xrep_port
260
261 pub_socket = context.socket(zmq.PUB)
262 pub_port = bind_port(pub_socket, namespace.ip, namespace.pub)
263 print >>sys.__stdout__, "PUB Channel on port", pub_port
264
265 req_socket = context.socket(zmq.XREQ)
266 req_port = bind_port(req_socket, namespace.ip, namespace.req)
267 print >>sys.__stdout__, "REQ Channel on port", req_port
268
269 # Redirect input streams. This needs to be done before the Kernel is done
270 # because currently the Kernel creates a ZMQInteractiveShell, which
271 # holds references to sys.stdout and sys.stderr.
272 sys.stdout = OutStream(session, pub_socket, u'stdout')
273 sys.stderr = OutStream(session, pub_socket, u'stderr')
274 # Set a displayhook.
275 sys.displayhook = DisplayHook(session, pub_socket)
276
277 # Create the kernel.
278 kernel = Kernel(
279 session=session, reply_socket=reply_socket,
280 pub_socket=pub_socket, req_socket=req_socket
281 )
282
283 # Configure this kernel/process to die on parent termination, if necessary.
284 if namespace.parent:
285 if sys.platform == 'win32':
286 poller = ExitPollerWindows(namespace.parent)
287 else:
288 poller = ExitPollerUnix()
289 poller.start()
290
291 # Start the kernel mainloop.
292 kernel.start()
293
294
295 def launch_kernel(xrep_port=0, pub_port=0, req_port=0, independent=False):
296 """ Launches a localhost kernel, binding to the specified ports.
307 """ Launches a localhost kernel, binding to the specified ports.
297
308
298 Parameters
309 Parameters
299 ----------
310 ----------
300 xrep_port : int, optional
311 xrep_port : int, optional
301 The port to use for XREP channel.
312 The port to use for XREP channel.
302
313
303 pub_port : int, optional
314 pub_port : int, optional
304 The port to use for the SUB channel.
315 The port to use for the SUB channel.
305
316
306 req_port : int, optional
317 req_port : int, optional
307 The port to use for the REQ (raw input) channel.
318 The port to use for the REQ (raw input) channel.
308
319
309 independent : bool, optional (default False)
320 independent : bool, optional (default False)
310 If set, the kernel process is guaranteed to survive if this process
321 If set, the kernel process is guaranteed to survive if this process
311 dies. If not set, an effort is made to ensure that the kernel is killed
322 dies. If not set, an effort is made to ensure that the kernel is killed
312 when this process dies. Note that in this case it is still good practice
323 when this process dies. Note that in this case it is still good practice
313 to kill kernels manually before exiting.
324 to kill kernels manually before exiting.
314
325
326 pylab : bool or string, optional (default False)
327 If not False, the kernel will be launched with pylab enabled. If a
328 string is passed, matplotlib will use the specified backend. Otherwise,
329 matplotlib's default backend will be used.
330
315 Returns
331 Returns
316 -------
332 -------
317 A tuple of form:
333 A tuple of form:
318 (kernel_process, xrep_port, pub_port, req_port)
334 (kernel_process, xrep_port, pub_port, req_port)
319 where kernel_process is a Popen object and the ports are integers.
335 where kernel_process is a Popen object and the ports are integers.
320 """
336 """
321 import socket
337 extra_arguments = []
322 from subprocess import Popen
338 if pylab:
323
339 extra_arguments.append('--pylab')
324 # Find open ports as necessary.
340 if isinstance(pylab, basestring):
325 ports = []
341 extra_arguments.append(pylab)
326 ports_needed = int(xrep_port <= 0) + int(pub_port <= 0) + int(req_port <= 0)
342 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
327 for i in xrange(ports_needed):
343 xrep_port, pub_port, req_port, independent,
328 sock = socket.socket()
344 extra_arguments)
329 sock.bind(('', 0))
330 ports.append(sock)
331 for i, sock in enumerate(ports):
332 port = sock.getsockname()[1]
333 sock.close()
334 ports[i] = port
335 if xrep_port <= 0:
336 xrep_port = ports.pop(0)
337 if pub_port <= 0:
338 pub_port = ports.pop(0)
339 if req_port <= 0:
340 req_port = ports.pop(0)
341
342 # Spawn a kernel.
343 command = 'from IPython.zmq.ipkernel import main; main()'
344 arguments = [ sys.executable, '-c', command, '--xrep', str(xrep_port),
345 '--pub', str(pub_port), '--req', str(req_port) ]
346 if independent:
347 if sys.platform == 'win32':
348 proc = Popen(['start', '/b'] + arguments, shell=True)
349 else:
350 proc = Popen(arguments, preexec_fn=lambda: os.setsid())
351 else:
352 if sys.platform == 'win32':
353 from _subprocess import DuplicateHandle, GetCurrentProcess, \
354 DUPLICATE_SAME_ACCESS
355 pid = GetCurrentProcess()
356 handle = DuplicateHandle(pid, pid, pid, 0,
357 True, # Inheritable by new processes.
358 DUPLICATE_SAME_ACCESS)
359 proc = Popen(arguments + ['--parent', str(int(handle))])
360 else:
361 proc = Popen(arguments + ['--parent'])
362
345
363 return proc, xrep_port, pub_port, req_port
346 def main():
347 """ The IPython kernel main entry point.
348 """
349 parser = make_argument_parser()
350 parser.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
351 const='auto', help = \
352 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
353 given, the GUI backend is matplotlib's, otherwise use one of: \
354 ['tk', 'gtk', 'qt', 'wx', 'payload-svg'].")
355 namespace = parser.parse_args()
356
357 kernel = make_kernel(namespace, Kernel)
358 if namespace.pylab:
359 if namespace.pylab == 'auto':
360 kernel.activate_pylab()
361 else:
362 kernel.activate_pylab(namespace.pylab)
364
363
364 start_kernel(namespace, kernel)
365
365
366 if __name__ == '__main__':
366 if __name__ == '__main__':
367 main()
367 main()
@@ -1,575 +1,578 b''
1 """Base classes to manage the interaction with a running kernel.
1 """Base classes to manage the interaction with a running kernel.
2
2
3 Todo
3 Todo
4 ====
4 ====
5
5
6 * Create logger to handle debugging and console messages.
6 * Create logger to handle debugging and console messages.
7 """
7 """
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2008-2010 The IPython Development Team
10 # Copyright (C) 2008-2010 The IPython Development Team
11 #
11 #
12 # Distributed under the terms of the BSD License. The full license is in
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
13 # the file COPYING, distributed as part of this software.
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 # Standard library imports.
20 # Standard library imports.
21 from Queue import Queue, Empty
21 from Queue import Queue, Empty
22 from subprocess import Popen
22 from subprocess import Popen
23 from threading import Thread
23 from threading import Thread
24 import time
24 import time
25
25
26 # System library imports.
26 # System library imports.
27 import zmq
27 import zmq
28 from zmq import POLLIN, POLLOUT, POLLERR
28 from zmq import POLLIN, POLLOUT, POLLERR
29 from zmq.eventloop import ioloop
29 from zmq.eventloop import ioloop
30
30
31 # Local imports.
31 # Local imports.
32 from IPython.utils.traitlets import HasTraits, Any, Instance, Type, TCPAddress
32 from IPython.utils.traitlets import HasTraits, Any, Instance, Type, TCPAddress
33 from ipkernel import launch_kernel
34 from session import Session
33 from session import Session
35
34
36 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
37 # Constants and exceptions
36 # Constants and exceptions
38 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
39
38
40 LOCALHOST = '127.0.0.1'
39 LOCALHOST = '127.0.0.1'
41
40
42 class InvalidPortNumber(Exception):
41 class InvalidPortNumber(Exception):
43 pass
42 pass
44
43
45 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
46 # ZMQ Socket Channel classes
45 # ZMQ Socket Channel classes
47 #-----------------------------------------------------------------------------
46 #-----------------------------------------------------------------------------
48
47
49 class ZmqSocketChannel(Thread):
48 class ZmqSocketChannel(Thread):
50 """The base class for the channels that use ZMQ sockets.
49 """The base class for the channels that use ZMQ sockets.
51 """
50 """
52 context = None
51 context = None
53 session = None
52 session = None
54 socket = None
53 socket = None
55 ioloop = None
54 ioloop = None
56 iostate = None
55 iostate = None
57 _address = None
56 _address = None
58
57
59 def __init__(self, context, session, address):
58 def __init__(self, context, session, address):
60 """Create a channel
59 """Create a channel
61
60
62 Parameters
61 Parameters
63 ----------
62 ----------
64 context : :class:`zmq.Context`
63 context : :class:`zmq.Context`
65 The ZMQ context to use.
64 The ZMQ context to use.
66 session : :class:`session.Session`
65 session : :class:`session.Session`
67 The session to use.
66 The session to use.
68 address : tuple
67 address : tuple
69 Standard (ip, port) tuple that the kernel is listening on.
68 Standard (ip, port) tuple that the kernel is listening on.
70 """
69 """
71 super(ZmqSocketChannel, self).__init__()
70 super(ZmqSocketChannel, self).__init__()
72 self.daemon = True
71 self.daemon = True
73
72
74 self.context = context
73 self.context = context
75 self.session = session
74 self.session = session
76 if address[1] == 0:
75 if address[1] == 0:
77 message = 'The port number for a channel cannot be 0.'
76 message = 'The port number for a channel cannot be 0.'
78 raise InvalidPortNumber(message)
77 raise InvalidPortNumber(message)
79 self._address = address
78 self._address = address
80
79
81 def stop(self):
80 def stop(self):
82 """Stop the channel's activity.
81 """Stop the channel's activity.
83
82
84 This calls :method:`Thread.join` and returns when the thread
83 This calls :method:`Thread.join` and returns when the thread
85 terminates. :class:`RuntimeError` will be raised if
84 terminates. :class:`RuntimeError` will be raised if
86 :method:`self.start` is called again.
85 :method:`self.start` is called again.
87 """
86 """
88 self.join()
87 self.join()
89
88
90 @property
89 @property
91 def address(self):
90 def address(self):
92 """Get the channel's address as an (ip, port) tuple.
91 """Get the channel's address as an (ip, port) tuple.
93
92
94 By the default, the address is (localhost, 0), where 0 means a random
93 By the default, the address is (localhost, 0), where 0 means a random
95 port.
94 port.
96 """
95 """
97 return self._address
96 return self._address
98
97
99 def add_io_state(self, state):
98 def add_io_state(self, state):
100 """Add IO state to the eventloop.
99 """Add IO state to the eventloop.
101
100
102 Parameters
101 Parameters
103 ----------
102 ----------
104 state : zmq.POLLIN|zmq.POLLOUT|zmq.POLLERR
103 state : zmq.POLLIN|zmq.POLLOUT|zmq.POLLERR
105 The IO state flag to set.
104 The IO state flag to set.
106
105
107 This is thread safe as it uses the thread safe IOLoop.add_callback.
106 This is thread safe as it uses the thread safe IOLoop.add_callback.
108 """
107 """
109 def add_io_state_callback():
108 def add_io_state_callback():
110 if not self.iostate & state:
109 if not self.iostate & state:
111 self.iostate = self.iostate | state
110 self.iostate = self.iostate | state
112 self.ioloop.update_handler(self.socket, self.iostate)
111 self.ioloop.update_handler(self.socket, self.iostate)
113 self.ioloop.add_callback(add_io_state_callback)
112 self.ioloop.add_callback(add_io_state_callback)
114
113
115 def drop_io_state(self, state):
114 def drop_io_state(self, state):
116 """Drop IO state from the eventloop.
115 """Drop IO state from the eventloop.
117
116
118 Parameters
117 Parameters
119 ----------
118 ----------
120 state : zmq.POLLIN|zmq.POLLOUT|zmq.POLLERR
119 state : zmq.POLLIN|zmq.POLLOUT|zmq.POLLERR
121 The IO state flag to set.
120 The IO state flag to set.
122
121
123 This is thread safe as it uses the thread safe IOLoop.add_callback.
122 This is thread safe as it uses the thread safe IOLoop.add_callback.
124 """
123 """
125 def drop_io_state_callback():
124 def drop_io_state_callback():
126 if self.iostate & state:
125 if self.iostate & state:
127 self.iostate = self.iostate & (~state)
126 self.iostate = self.iostate & (~state)
128 self.ioloop.update_handler(self.socket, self.iostate)
127 self.ioloop.update_handler(self.socket, self.iostate)
129 self.ioloop.add_callback(drop_io_state_callback)
128 self.ioloop.add_callback(drop_io_state_callback)
130
129
131
130
132 class XReqSocketChannel(ZmqSocketChannel):
131 class XReqSocketChannel(ZmqSocketChannel):
133 """The XREQ channel for issues request/replies to the kernel.
132 """The XREQ channel for issues request/replies to the kernel.
134 """
133 """
135
134
136 command_queue = None
135 command_queue = None
137
136
138 def __init__(self, context, session, address):
137 def __init__(self, context, session, address):
139 self.command_queue = Queue()
138 self.command_queue = Queue()
140 super(XReqSocketChannel, self).__init__(context, session, address)
139 super(XReqSocketChannel, self).__init__(context, session, address)
141
140
142 def run(self):
141 def run(self):
143 """The thread's main activity. Call start() instead."""
142 """The thread's main activity. Call start() instead."""
144 self.socket = self.context.socket(zmq.XREQ)
143 self.socket = self.context.socket(zmq.XREQ)
145 self.socket.setsockopt(zmq.IDENTITY, self.session.session)
144 self.socket.setsockopt(zmq.IDENTITY, self.session.session)
146 self.socket.connect('tcp://%s:%i' % self.address)
145 self.socket.connect('tcp://%s:%i' % self.address)
147 self.ioloop = ioloop.IOLoop()
146 self.ioloop = ioloop.IOLoop()
148 self.iostate = POLLERR|POLLIN
147 self.iostate = POLLERR|POLLIN
149 self.ioloop.add_handler(self.socket, self._handle_events,
148 self.ioloop.add_handler(self.socket, self._handle_events,
150 self.iostate)
149 self.iostate)
151 self.ioloop.start()
150 self.ioloop.start()
152
151
153 def stop(self):
152 def stop(self):
154 self.ioloop.stop()
153 self.ioloop.stop()
155 super(XReqSocketChannel, self).stop()
154 super(XReqSocketChannel, self).stop()
156
155
157 def call_handlers(self, msg):
156 def call_handlers(self, msg):
158 """This method is called in the ioloop thread when a message arrives.
157 """This method is called in the ioloop thread when a message arrives.
159
158
160 Subclasses should override this method to handle incoming messages.
159 Subclasses should override this method to handle incoming messages.
161 It is important to remember that this method is called in the thread
160 It is important to remember that this method is called in the thread
162 so that some logic must be done to ensure that the application leve
161 so that some logic must be done to ensure that the application leve
163 handlers are called in the application thread.
162 handlers are called in the application thread.
164 """
163 """
165 raise NotImplementedError('call_handlers must be defined in a subclass.')
164 raise NotImplementedError('call_handlers must be defined in a subclass.')
166
165
167 def execute(self, code):
166 def execute(self, code):
168 """Execute code in the kernel.
167 """Execute code in the kernel.
169
168
170 Parameters
169 Parameters
171 ----------
170 ----------
172 code : str
171 code : str
173 A string of Python code.
172 A string of Python code.
174
173
175 Returns
174 Returns
176 -------
175 -------
177 The msg_id of the message sent.
176 The msg_id of the message sent.
178 """
177 """
179 # Create class for content/msg creation. Related to, but possibly
178 # Create class for content/msg creation. Related to, but possibly
180 # not in Session.
179 # not in Session.
181 content = dict(code=code)
180 content = dict(code=code)
182 msg = self.session.msg('execute_request', content)
181 msg = self.session.msg('execute_request', content)
183 self._queue_request(msg)
182 self._queue_request(msg)
184 return msg['header']['msg_id']
183 return msg['header']['msg_id']
185
184
186 def complete(self, text, line, block=None):
185 def complete(self, text, line, block=None):
187 """Tab complete text, line, block in the kernel's namespace.
186 """Tab complete text, line, block in the kernel's namespace.
188
187
189 Parameters
188 Parameters
190 ----------
189 ----------
191 text : str
190 text : str
192 The text to complete.
191 The text to complete.
193 line : str
192 line : str
194 The full line of text that is the surrounding context for the
193 The full line of text that is the surrounding context for the
195 text to complete.
194 text to complete.
196 block : str
195 block : str
197 The full block of code in which the completion is being requested.
196 The full block of code in which the completion is being requested.
198
197
199 Returns
198 Returns
200 -------
199 -------
201 The msg_id of the message sent.
200 The msg_id of the message sent.
202 """
201 """
203 content = dict(text=text, line=line)
202 content = dict(text=text, line=line)
204 msg = self.session.msg('complete_request', content)
203 msg = self.session.msg('complete_request', content)
205 self._queue_request(msg)
204 self._queue_request(msg)
206 return msg['header']['msg_id']
205 return msg['header']['msg_id']
207
206
208 def object_info(self, oname):
207 def object_info(self, oname):
209 """Get metadata information about an object.
208 """Get metadata information about an object.
210
209
211 Parameters
210 Parameters
212 ----------
211 ----------
213 oname : str
212 oname : str
214 A string specifying the object name.
213 A string specifying the object name.
215
214
216 Returns
215 Returns
217 -------
216 -------
218 The msg_id of the message sent.
217 The msg_id of the message sent.
219 """
218 """
220 content = dict(oname=oname)
219 content = dict(oname=oname)
221 msg = self.session.msg('object_info_request', content)
220 msg = self.session.msg('object_info_request', content)
222 self._queue_request(msg)
221 self._queue_request(msg)
223 return msg['header']['msg_id']
222 return msg['header']['msg_id']
224
223
225 def _handle_events(self, socket, events):
224 def _handle_events(self, socket, events):
226 if events & POLLERR:
225 if events & POLLERR:
227 self._handle_err()
226 self._handle_err()
228 if events & POLLOUT:
227 if events & POLLOUT:
229 self._handle_send()
228 self._handle_send()
230 if events & POLLIN:
229 if events & POLLIN:
231 self._handle_recv()
230 self._handle_recv()
232
231
233 def _handle_recv(self):
232 def _handle_recv(self):
234 msg = self.socket.recv_json()
233 msg = self.socket.recv_json()
235 self.call_handlers(msg)
234 self.call_handlers(msg)
236
235
237 def _handle_send(self):
236 def _handle_send(self):
238 try:
237 try:
239 msg = self.command_queue.get(False)
238 msg = self.command_queue.get(False)
240 except Empty:
239 except Empty:
241 pass
240 pass
242 else:
241 else:
243 self.socket.send_json(msg)
242 self.socket.send_json(msg)
244 if self.command_queue.empty():
243 if self.command_queue.empty():
245 self.drop_io_state(POLLOUT)
244 self.drop_io_state(POLLOUT)
246
245
247 def _handle_err(self):
246 def _handle_err(self):
248 # We don't want to let this go silently, so eventually we should log.
247 # We don't want to let this go silently, so eventually we should log.
249 raise zmq.ZMQError()
248 raise zmq.ZMQError()
250
249
251 def _queue_request(self, msg):
250 def _queue_request(self, msg):
252 self.command_queue.put(msg)
251 self.command_queue.put(msg)
253 self.add_io_state(POLLOUT)
252 self.add_io_state(POLLOUT)
254
253
255
254
256 class SubSocketChannel(ZmqSocketChannel):
255 class SubSocketChannel(ZmqSocketChannel):
257 """The SUB channel which listens for messages that the kernel publishes.
256 """The SUB channel which listens for messages that the kernel publishes.
258 """
257 """
259
258
260 def __init__(self, context, session, address):
259 def __init__(self, context, session, address):
261 super(SubSocketChannel, self).__init__(context, session, address)
260 super(SubSocketChannel, self).__init__(context, session, address)
262
261
263 def run(self):
262 def run(self):
264 """The thread's main activity. Call start() instead."""
263 """The thread's main activity. Call start() instead."""
265 self.socket = self.context.socket(zmq.SUB)
264 self.socket = self.context.socket(zmq.SUB)
266 self.socket.setsockopt(zmq.SUBSCRIBE,'')
265 self.socket.setsockopt(zmq.SUBSCRIBE,'')
267 self.socket.setsockopt(zmq.IDENTITY, self.session.session)
266 self.socket.setsockopt(zmq.IDENTITY, self.session.session)
268 self.socket.connect('tcp://%s:%i' % self.address)
267 self.socket.connect('tcp://%s:%i' % self.address)
269 self.ioloop = ioloop.IOLoop()
268 self.ioloop = ioloop.IOLoop()
270 self.iostate = POLLIN|POLLERR
269 self.iostate = POLLIN|POLLERR
271 self.ioloop.add_handler(self.socket, self._handle_events,
270 self.ioloop.add_handler(self.socket, self._handle_events,
272 self.iostate)
271 self.iostate)
273 self.ioloop.start()
272 self.ioloop.start()
274
273
275 def stop(self):
274 def stop(self):
276 self.ioloop.stop()
275 self.ioloop.stop()
277 super(SubSocketChannel, self).stop()
276 super(SubSocketChannel, self).stop()
278
277
279 def call_handlers(self, msg):
278 def call_handlers(self, msg):
280 """This method is called in the ioloop thread when a message arrives.
279 """This method is called in the ioloop thread when a message arrives.
281
280
282 Subclasses should override this method to handle incoming messages.
281 Subclasses should override this method to handle incoming messages.
283 It is important to remember that this method is called in the thread
282 It is important to remember that this method is called in the thread
284 so that some logic must be done to ensure that the application leve
283 so that some logic must be done to ensure that the application leve
285 handlers are called in the application thread.
284 handlers are called in the application thread.
286 """
285 """
287 raise NotImplementedError('call_handlers must be defined in a subclass.')
286 raise NotImplementedError('call_handlers must be defined in a subclass.')
288
287
289 def flush(self, timeout=1.0):
288 def flush(self, timeout=1.0):
290 """Immediately processes all pending messages on the SUB channel.
289 """Immediately processes all pending messages on the SUB channel.
291
290
292 Callers should use this method to ensure that :method:`call_handlers`
291 Callers should use this method to ensure that :method:`call_handlers`
293 has been called for all messages that have been received on the
292 has been called for all messages that have been received on the
294 0MQ SUB socket of this channel.
293 0MQ SUB socket of this channel.
295
294
296 This method is thread safe.
295 This method is thread safe.
297
296
298 Parameters
297 Parameters
299 ----------
298 ----------
300 timeout : float, optional
299 timeout : float, optional
301 The maximum amount of time to spend flushing, in seconds. The
300 The maximum amount of time to spend flushing, in seconds. The
302 default is one second.
301 default is one second.
303 """
302 """
304 # We do the IOLoop callback process twice to ensure that the IOLoop
303 # We do the IOLoop callback process twice to ensure that the IOLoop
305 # gets to perform at least one full poll.
304 # gets to perform at least one full poll.
306 stop_time = time.time() + timeout
305 stop_time = time.time() + timeout
307 for i in xrange(2):
306 for i in xrange(2):
308 self._flushed = False
307 self._flushed = False
309 self.ioloop.add_callback(self._flush)
308 self.ioloop.add_callback(self._flush)
310 while not self._flushed and time.time() < stop_time:
309 while not self._flushed and time.time() < stop_time:
311 time.sleep(0.01)
310 time.sleep(0.01)
312
311
313 def _handle_events(self, socket, events):
312 def _handle_events(self, socket, events):
314 # Turn on and off POLLOUT depending on if we have made a request
313 # Turn on and off POLLOUT depending on if we have made a request
315 if events & POLLERR:
314 if events & POLLERR:
316 self._handle_err()
315 self._handle_err()
317 if events & POLLIN:
316 if events & POLLIN:
318 self._handle_recv()
317 self._handle_recv()
319
318
320 def _handle_err(self):
319 def _handle_err(self):
321 # We don't want to let this go silently, so eventually we should log.
320 # We don't want to let this go silently, so eventually we should log.
322 raise zmq.ZMQError()
321 raise zmq.ZMQError()
323
322
324 def _handle_recv(self):
323 def _handle_recv(self):
325 # Get all of the messages we can
324 # Get all of the messages we can
326 while True:
325 while True:
327 try:
326 try:
328 msg = self.socket.recv_json(zmq.NOBLOCK)
327 msg = self.socket.recv_json(zmq.NOBLOCK)
329 except zmq.ZMQError:
328 except zmq.ZMQError:
330 # Check the errno?
329 # Check the errno?
331 # Will this trigger POLLERR?
330 # Will this trigger POLLERR?
332 break
331 break
333 else:
332 else:
334 self.call_handlers(msg)
333 self.call_handlers(msg)
335
334
336 def _flush(self):
335 def _flush(self):
337 """Callback for :method:`self.flush`."""
336 """Callback for :method:`self.flush`."""
338 self._flushed = True
337 self._flushed = True
339
338
340
339
341 class RepSocketChannel(ZmqSocketChannel):
340 class RepSocketChannel(ZmqSocketChannel):
342 """A reply channel to handle raw_input requests that the kernel makes."""
341 """A reply channel to handle raw_input requests that the kernel makes."""
343
342
344 msg_queue = None
343 msg_queue = None
345
344
346 def __init__(self, context, session, address):
345 def __init__(self, context, session, address):
347 self.msg_queue = Queue()
346 self.msg_queue = Queue()
348 super(RepSocketChannel, self).__init__(context, session, address)
347 super(RepSocketChannel, self).__init__(context, session, address)
349
348
350 def run(self):
349 def run(self):
351 """The thread's main activity. Call start() instead."""
350 """The thread's main activity. Call start() instead."""
352 self.socket = self.context.socket(zmq.XREQ)
351 self.socket = self.context.socket(zmq.XREQ)
353 self.socket.setsockopt(zmq.IDENTITY, self.session.session)
352 self.socket.setsockopt(zmq.IDENTITY, self.session.session)
354 self.socket.connect('tcp://%s:%i' % self.address)
353 self.socket.connect('tcp://%s:%i' % self.address)
355 self.ioloop = ioloop.IOLoop()
354 self.ioloop = ioloop.IOLoop()
356 self.iostate = POLLERR|POLLIN
355 self.iostate = POLLERR|POLLIN
357 self.ioloop.add_handler(self.socket, self._handle_events,
356 self.ioloop.add_handler(self.socket, self._handle_events,
358 self.iostate)
357 self.iostate)
359 self.ioloop.start()
358 self.ioloop.start()
360
359
361 def stop(self):
360 def stop(self):
362 self.ioloop.stop()
361 self.ioloop.stop()
363 super(RepSocketChannel, self).stop()
362 super(RepSocketChannel, self).stop()
364
363
365 def call_handlers(self, msg):
364 def call_handlers(self, msg):
366 """This method is called in the ioloop thread when a message arrives.
365 """This method is called in the ioloop thread when a message arrives.
367
366
368 Subclasses should override this method to handle incoming messages.
367 Subclasses should override this method to handle incoming messages.
369 It is important to remember that this method is called in the thread
368 It is important to remember that this method is called in the thread
370 so that some logic must be done to ensure that the application leve
369 so that some logic must be done to ensure that the application leve
371 handlers are called in the application thread.
370 handlers are called in the application thread.
372 """
371 """
373 raise NotImplementedError('call_handlers must be defined in a subclass.')
372 raise NotImplementedError('call_handlers must be defined in a subclass.')
374
373
375 def input(self, string):
374 def input(self, string):
376 """Send a string of raw input to the kernel."""
375 """Send a string of raw input to the kernel."""
377 content = dict(value=string)
376 content = dict(value=string)
378 msg = self.session.msg('input_reply', content)
377 msg = self.session.msg('input_reply', content)
379 self._queue_reply(msg)
378 self._queue_reply(msg)
380
379
381 def _handle_events(self, socket, events):
380 def _handle_events(self, socket, events):
382 if events & POLLERR:
381 if events & POLLERR:
383 self._handle_err()
382 self._handle_err()
384 if events & POLLOUT:
383 if events & POLLOUT:
385 self._handle_send()
384 self._handle_send()
386 if events & POLLIN:
385 if events & POLLIN:
387 self._handle_recv()
386 self._handle_recv()
388
387
389 def _handle_recv(self):
388 def _handle_recv(self):
390 msg = self.socket.recv_json()
389 msg = self.socket.recv_json()
391 self.call_handlers(msg)
390 self.call_handlers(msg)
392
391
393 def _handle_send(self):
392 def _handle_send(self):
394 try:
393 try:
395 msg = self.msg_queue.get(False)
394 msg = self.msg_queue.get(False)
396 except Empty:
395 except Empty:
397 pass
396 pass
398 else:
397 else:
399 self.socket.send_json(msg)
398 self.socket.send_json(msg)
400 if self.msg_queue.empty():
399 if self.msg_queue.empty():
401 self.drop_io_state(POLLOUT)
400 self.drop_io_state(POLLOUT)
402
401
403 def _handle_err(self):
402 def _handle_err(self):
404 # We don't want to let this go silently, so eventually we should log.
403 # We don't want to let this go silently, so eventually we should log.
405 raise zmq.ZMQError()
404 raise zmq.ZMQError()
406
405
407 def _queue_reply(self, msg):
406 def _queue_reply(self, msg):
408 self.msg_queue.put(msg)
407 self.msg_queue.put(msg)
409 self.add_io_state(POLLOUT)
408 self.add_io_state(POLLOUT)
410
409
411
410
412 #-----------------------------------------------------------------------------
411 #-----------------------------------------------------------------------------
413 # Main kernel manager class
412 # Main kernel manager class
414 #-----------------------------------------------------------------------------
413 #-----------------------------------------------------------------------------
415
414
416 class KernelManager(HasTraits):
415 class KernelManager(HasTraits):
417 """ Manages a kernel for a frontend.
416 """ Manages a kernel for a frontend.
418
417
419 The SUB channel is for the frontend to receive messages published by the
418 The SUB channel is for the frontend to receive messages published by the
420 kernel.
419 kernel.
421
420
422 The REQ channel is for the frontend to make requests of the kernel.
421 The REQ channel is for the frontend to make requests of the kernel.
423
422
424 The REP channel is for the kernel to request stdin (raw_input) from the
423 The REP channel is for the kernel to request stdin (raw_input) from the
425 frontend.
424 frontend.
426 """
425 """
427 # The PyZMQ Context to use for communication with the kernel.
426 # The PyZMQ Context to use for communication with the kernel.
428 context = Instance(zmq.Context,(),{})
427 context = Instance(zmq.Context,(),{})
429
428
430 # The Session to use for communication with the kernel.
429 # The Session to use for communication with the kernel.
431 session = Instance(Session,(),{})
430 session = Instance(Session,(),{})
432
431
433 # The kernel process with which the KernelManager is communicating.
432 # The kernel process with which the KernelManager is communicating.
434 kernel = Instance(Popen)
433 kernel = Instance(Popen)
435
434
436 # The addresses for the communication channels.
435 # The addresses for the communication channels.
437 xreq_address = TCPAddress((LOCALHOST, 0))
436 xreq_address = TCPAddress((LOCALHOST, 0))
438 sub_address = TCPAddress((LOCALHOST, 0))
437 sub_address = TCPAddress((LOCALHOST, 0))
439 rep_address = TCPAddress((LOCALHOST, 0))
438 rep_address = TCPAddress((LOCALHOST, 0))
440
439
441 # The classes to use for the various channels.
440 # The classes to use for the various channels.
442 xreq_channel_class = Type(XReqSocketChannel)
441 xreq_channel_class = Type(XReqSocketChannel)
443 sub_channel_class = Type(SubSocketChannel)
442 sub_channel_class = Type(SubSocketChannel)
444 rep_channel_class = Type(RepSocketChannel)
443 rep_channel_class = Type(RepSocketChannel)
445
444
446 # Protected traits.
445 # Protected traits.
447 _xreq_channel = Any
446 _xreq_channel = Any
448 _sub_channel = Any
447 _sub_channel = Any
449 _rep_channel = Any
448 _rep_channel = Any
450
449
451 #--------------------------------------------------------------------------
450 #--------------------------------------------------------------------------
452 # Channel management methods:
451 # Channel management methods:
453 #--------------------------------------------------------------------------
452 #--------------------------------------------------------------------------
454
453
455 def start_channels(self):
454 def start_channels(self):
456 """Starts the channels for this kernel.
455 """Starts the channels for this kernel.
457
456
458 This will create the channels if they do not exist and then start
457 This will create the channels if they do not exist and then start
459 them. If port numbers of 0 are being used (random ports) then you
458 them. If port numbers of 0 are being used (random ports) then you
460 must first call :method:`start_kernel`. If the channels have been
459 must first call :method:`start_kernel`. If the channels have been
461 stopped and you call this, :class:`RuntimeError` will be raised.
460 stopped and you call this, :class:`RuntimeError` will be raised.
462 """
461 """
463 self.xreq_channel.start()
462 self.xreq_channel.start()
464 self.sub_channel.start()
463 self.sub_channel.start()
465 self.rep_channel.start()
464 self.rep_channel.start()
466
465
467 def stop_channels(self):
466 def stop_channels(self):
468 """Stops the channels for this kernel.
467 """Stops the channels for this kernel.
469
468
470 This stops the channels by joining their threads. If the channels
469 This stops the channels by joining their threads. If the channels
471 were not started, :class:`RuntimeError` will be raised.
470 were not started, :class:`RuntimeError` will be raised.
472 """
471 """
473 self.xreq_channel.stop()
472 self.xreq_channel.stop()
474 self.sub_channel.stop()
473 self.sub_channel.stop()
475 self.rep_channel.stop()
474 self.rep_channel.stop()
476
475
477 @property
476 @property
478 def channels_running(self):
477 def channels_running(self):
479 """Are all of the channels created and running?"""
478 """Are all of the channels created and running?"""
480 return self.xreq_channel.is_alive() \
479 return self.xreq_channel.is_alive() \
481 and self.sub_channel.is_alive() \
480 and self.sub_channel.is_alive() \
482 and self.rep_channel.is_alive()
481 and self.rep_channel.is_alive()
483
482
484 #--------------------------------------------------------------------------
483 #--------------------------------------------------------------------------
485 # Kernel process management methods:
484 # Kernel process management methods:
486 #--------------------------------------------------------------------------
485 #--------------------------------------------------------------------------
487
486
488 def start_kernel(self, pylab=False):
487 def start_kernel(self, ipython=True, **kw):
489 """Starts a kernel process and configures the manager to use it.
488 """Starts a kernel process and configures the manager to use it.
490
489
491 If random ports (port=0) are being used, this method must be called
490 If random ports (port=0) are being used, this method must be called
492 before the channels are created.
491 before the channels are created.
493
492
494 Parameters:
493 Parameters:
495 -----------
494 -----------
496 pylab : bool or string, optional (default False)
495 ipython : bool, optional (default True)
497 See IPython.zmq.kernel.launch_kernel for documentation.
496 Whether to use an IPython kernel instead of a plain Python kernel.
498 """
497 """
499 xreq, sub, rep = self.xreq_address, self.sub_address, self.rep_address
498 xreq, sub, rep = self.xreq_address, self.sub_address, self.rep_address
500 if xreq[0] != LOCALHOST or sub[0] != LOCALHOST or rep[0] != LOCALHOST:
499 if xreq[0] != LOCALHOST or sub[0] != LOCALHOST or rep[0] != LOCALHOST:
501 raise RuntimeError("Can only launch a kernel on localhost."
500 raise RuntimeError("Can only launch a kernel on localhost."
502 "Make sure that the '*_address' attributes are "
501 "Make sure that the '*_address' attributes are "
503 "configured properly.")
502 "configured properly.")
504
503
505 self.kernel, xrep, pub, req = launch_kernel(
504 if ipython:
506 xrep_port=xreq[1], pub_port=sub[1], req_port=rep[1], pylab=pylab)
505 from ipkernel import launch_kernel as launch
506 else:
507 from pykernel import launch_kernel as launch
508 self.kernel, xrep, pub, req = launch(xrep_port=xreq[1], pub_port=sub[1],
509 req_port=rep[1], **kw)
507 self.xreq_address = (LOCALHOST, xrep)
510 self.xreq_address = (LOCALHOST, xrep)
508 self.sub_address = (LOCALHOST, pub)
511 self.sub_address = (LOCALHOST, pub)
509 self.rep_address = (LOCALHOST, req)
512 self.rep_address = (LOCALHOST, req)
510
513
511 @property
514 @property
512 def has_kernel(self):
515 def has_kernel(self):
513 """Returns whether a kernel process has been specified for the kernel
516 """Returns whether a kernel process has been specified for the kernel
514 manager.
517 manager.
515 """
518 """
516 return self.kernel is not None
519 return self.kernel is not None
517
520
518 def kill_kernel(self):
521 def kill_kernel(self):
519 """ Kill the running kernel. """
522 """ Kill the running kernel. """
520 if self.kernel is not None:
523 if self.kernel is not None:
521 self.kernel.kill()
524 self.kernel.kill()
522 self.kernel = None
525 self.kernel = None
523 else:
526 else:
524 raise RuntimeError("Cannot kill kernel. No kernel is running!")
527 raise RuntimeError("Cannot kill kernel. No kernel is running!")
525
528
526 def signal_kernel(self, signum):
529 def signal_kernel(self, signum):
527 """ Sends a signal to the kernel. """
530 """ Sends a signal to the kernel. """
528 if self.kernel is not None:
531 if self.kernel is not None:
529 self.kernel.send_signal(signum)
532 self.kernel.send_signal(signum)
530 else:
533 else:
531 raise RuntimeError("Cannot signal kernel. No kernel is running!")
534 raise RuntimeError("Cannot signal kernel. No kernel is running!")
532
535
533 @property
536 @property
534 def is_alive(self):
537 def is_alive(self):
535 """Is the kernel process still running?"""
538 """Is the kernel process still running?"""
536 if self.kernel is not None:
539 if self.kernel is not None:
537 if self.kernel.poll() is None:
540 if self.kernel.poll() is None:
538 return True
541 return True
539 else:
542 else:
540 return False
543 return False
541 else:
544 else:
542 # We didn't start the kernel with this KernelManager so we don't
545 # We didn't start the kernel with this KernelManager so we don't
543 # know if it is running. We should use a heartbeat for this case.
546 # know if it is running. We should use a heartbeat for this case.
544 return True
547 return True
545
548
546 #--------------------------------------------------------------------------
549 #--------------------------------------------------------------------------
547 # Channels used for communication with the kernel:
550 # Channels used for communication with the kernel:
548 #--------------------------------------------------------------------------
551 #--------------------------------------------------------------------------
549
552
550 @property
553 @property
551 def xreq_channel(self):
554 def xreq_channel(self):
552 """Get the REQ socket channel object to make requests of the kernel."""
555 """Get the REQ socket channel object to make requests of the kernel."""
553 if self._xreq_channel is None:
556 if self._xreq_channel is None:
554 self._xreq_channel = self.xreq_channel_class(self.context,
557 self._xreq_channel = self.xreq_channel_class(self.context,
555 self.session,
558 self.session,
556 self.xreq_address)
559 self.xreq_address)
557 return self._xreq_channel
560 return self._xreq_channel
558
561
559 @property
562 @property
560 def sub_channel(self):
563 def sub_channel(self):
561 """Get the SUB socket channel object."""
564 """Get the SUB socket channel object."""
562 if self._sub_channel is None:
565 if self._sub_channel is None:
563 self._sub_channel = self.sub_channel_class(self.context,
566 self._sub_channel = self.sub_channel_class(self.context,
564 self.session,
567 self.session,
565 self.sub_address)
568 self.sub_address)
566 return self._sub_channel
569 return self._sub_channel
567
570
568 @property
571 @property
569 def rep_channel(self):
572 def rep_channel(self):
570 """Get the REP socket channel object to handle stdin (raw_input)."""
573 """Get the REP socket channel object to handle stdin (raw_input)."""
571 if self._rep_channel is None:
574 if self._rep_channel is None:
572 self._rep_channel = self.rep_channel_class(self.context,
575 self._rep_channel = self.rep_channel_class(self.context,
573 self.session,
576 self.session,
574 self.rep_address)
577 self.rep_address)
575 return self._rep_channel
578 return self._rep_channel
@@ -1,472 +1,260 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """A simple interactive kernel that talks to a frontend over 0MQ.
2 """A simple interactive kernel that talks to a frontend over 0MQ.
3
3
4 Things to do:
4 Things to do:
5
5
6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
7 call set_parent on all the PUB objects with the message about to be executed.
7 call set_parent on all the PUB objects with the message about to be executed.
8 * Implement random port and security key logic.
8 * Implement random port and security key logic.
9 * Implement control messages.
9 * Implement control messages.
10 * Implement event loop and poll version.
10 * Implement event loop and poll version.
11 """
11 """
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 # Standard library imports.
17 # Standard library imports.
18 import __builtin__
18 import __builtin__
19 from code import CommandCompiler
19 from code import CommandCompiler
20 import os
21 import sys
20 import sys
22 import time
21 import time
23 import traceback
22 import traceback
24
23
25 # System library imports.
24 # System library imports.
26 import zmq
25 import zmq
27
26
28 # Local imports.
27 # Local imports.
29 from IPython.external.argparse import ArgumentParser
28 from IPython.utils.traitlets import HasTraits, Instance
30 from session import Session, Message
31 from completer import KernelCompleter
29 from completer import KernelCompleter
32 from iostream import OutStream
30 from entry_point import base_launch_kernel, make_default_main
33 from displayhook import DisplayHook
31 from session import Session, Message
34 from exitpoller import ExitPollerUnix, ExitPollerWindows
35
32
36 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
37 # Main kernel class
34 # Main kernel class
38 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
39
36
40 class Kernel(object):
37 class Kernel(HasTraits):
41
42 # The global kernel instance.
43 _kernel = None
44
45 # Maps user-friendly backend names to matplotlib backend identifiers.
46 _pylab_map = { 'tk': 'TkAgg',
47 'gtk': 'GTKAgg',
48 'wx': 'WXAgg',
49 'qt': 'Qt4Agg', # qt3 not supported
50 'qt4': 'Qt4Agg',
51 'payload-svg' : \
52 'module://IPython.zmq.pylab.backend_payload_svg' }
53
38
54 #---------------------------------------------------------------------------
39 #---------------------------------------------------------------------------
55 # Kernel interface
40 # Kernel interface
56 #---------------------------------------------------------------------------
41 #---------------------------------------------------------------------------
57
42
58 def __init__(self, session, reply_socket, pub_socket, req_socket):
43 session = Instance(Session)
59 self.session = session
44 reply_socket = Instance('zmq.Socket')
60 self.reply_socket = reply_socket
45 pub_socket = Instance('zmq.Socket')
61 self.pub_socket = pub_socket
46 req_socket = Instance('zmq.Socket')
62 self.req_socket = req_socket
47
48 def __init__(self, **kwargs):
49 super(Kernel, self).__init__(**kwargs)
63 self.user_ns = {}
50 self.user_ns = {}
64 self.history = []
51 self.history = []
65 self.compiler = CommandCompiler()
52 self.compiler = CommandCompiler()
66 self.completer = KernelCompleter(self.user_ns)
53 self.completer = KernelCompleter(self.user_ns)
67
54
68 # Protected variables.
69 self._exec_payload = {}
70
71 # Build dict of handlers for message types
55 # Build dict of handlers for message types
72 msg_types = [ 'execute_request', 'complete_request',
56 msg_types = [ 'execute_request', 'complete_request',
73 'object_info_request' ]
57 'object_info_request' ]
74 self.handlers = {}
58 self.handlers = {}
75 for msg_type in msg_types:
59 for msg_type in msg_types:
76 self.handlers[msg_type] = getattr(self, msg_type)
60 self.handlers[msg_type] = getattr(self, msg_type)
77
61
78 def add_exec_payload(self, key, value):
79 """ Adds a key/value pair to the execute payload.
80 """
81 self._exec_payload[key] = value
82
83 def activate_pylab(self, backend=None, import_all=True):
84 """ Activates pylab in this kernel's namespace.
85
86 Parameters:
87 -----------
88 backend : str, optional
89 A valid backend name.
90
91 import_all : bool, optional
92 If true, an 'import *' is done from numpy and pylab.
93 """
94 # FIXME: This is adapted from IPython.lib.pylabtools.pylab_activate.
95 # Common funtionality should be refactored.
96
97 # We must set the desired backend before importing pylab.
98 import matplotlib
99 if backend:
100 backend_id = self._pylab_map[backend]
101 if backend_id.startswith('module://'):
102 # Work around bug in matplotlib: matplotlib.use converts the
103 # backend_id to lowercase even if a module name is specified!
104 matplotlib.rcParams['backend'] = backend_id
105 else:
106 matplotlib.use(backend_id)
107
108 # Import numpy as np/pyplot as plt are conventions we're trying to
109 # somewhat standardize on. Making them available to users by default
110 # will greatly help this.
111 exec ("import numpy\n"
112 "import matplotlib\n"
113 "from matplotlib import pylab, mlab, pyplot\n"
114 "np = numpy\n"
115 "plt = pyplot\n"
116 ) in self.user_ns
117
118 if import_all:
119 exec("from matplotlib.pylab import *\n"
120 "from numpy import *\n") in self.user_ns
121
122 matplotlib.interactive(True)
123
124 @classmethod
125 def get_kernel(cls):
126 """ Return the global kernel instance or raise a RuntimeError if it does
127 not exist.
128 """
129 if cls._kernel is None:
130 raise RuntimeError("Kernel not started!")
131 else:
132 return cls._kernel
133
134 def start(self):
62 def start(self):
135 """ Start the kernel main loop.
63 """ Start the kernel main loop.
136 """
64 """
137 # Set the global kernel instance.
138 Kernel._kernel = self
139
140 while True:
65 while True:
141 ident = self.reply_socket.recv()
66 ident = self.reply_socket.recv()
142 assert self.reply_socket.rcvmore(), "Missing message part."
67 assert self.reply_socket.rcvmore(), "Missing message part."
143 msg = self.reply_socket.recv_json()
68 msg = self.reply_socket.recv_json()
144 omsg = Message(msg)
69 omsg = Message(msg)
145 print>>sys.__stdout__
70 print>>sys.__stdout__
146 print>>sys.__stdout__, omsg
71 print>>sys.__stdout__, omsg
147 handler = self.handlers.get(omsg.msg_type, None)
72 handler = self.handlers.get(omsg.msg_type, None)
148 if handler is None:
73 if handler is None:
149 print >> sys.__stderr__, "UNKNOWN MESSAGE TYPE:", omsg
74 print >> sys.__stderr__, "UNKNOWN MESSAGE TYPE:", omsg
150 else:
75 else:
151 handler(ident, omsg)
76 handler(ident, omsg)
152
77
153 #---------------------------------------------------------------------------
78 #---------------------------------------------------------------------------
154 # Kernel request handlers
79 # Kernel request handlers
155 #---------------------------------------------------------------------------
80 #---------------------------------------------------------------------------
156
81
157 def execute_request(self, ident, parent):
82 def execute_request(self, ident, parent):
158 try:
83 try:
159 code = parent[u'content'][u'code']
84 code = parent[u'content'][u'code']
160 except:
85 except:
161 print>>sys.__stderr__, "Got bad msg: "
86 print>>sys.__stderr__, "Got bad msg: "
162 print>>sys.__stderr__, Message(parent)
87 print>>sys.__stderr__, Message(parent)
163 return
88 return
164 pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
89 pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
165 self.pub_socket.send_json(pyin_msg)
90 self.pub_socket.send_json(pyin_msg)
166
91
167 # Clear the execute payload from the last request.
168 self._exec_payload = {}
169
170 try:
92 try:
171 comp_code = self.compiler(code, '<zmq-kernel>')
93 comp_code = self.compiler(code, '<zmq-kernel>')
172
94
173 # Replace raw_input. Note that is not sufficient to replace
95 # Replace raw_input. Note that is not sufficient to replace
174 # raw_input in the user namespace.
96 # raw_input in the user namespace.
175 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
97 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
176 __builtin__.raw_input = raw_input
98 __builtin__.raw_input = raw_input
177
99
178 # Configure the display hook.
100 # Configure the display hook.
179 sys.displayhook.set_parent(parent)
101 sys.displayhook.set_parent(parent)
180
102
181 exec comp_code in self.user_ns, self.user_ns
103 exec comp_code in self.user_ns, self.user_ns
182 except:
104 except:
183 etype, evalue, tb = sys.exc_info()
105 etype, evalue, tb = sys.exc_info()
184 tb = traceback.format_exception(etype, evalue, tb)
106 tb = traceback.format_exception(etype, evalue, tb)
185 exc_content = {
107 exc_content = {
186 u'status' : u'error',
108 u'status' : u'error',
187 u'traceback' : tb,
109 u'traceback' : tb,
188 u'ename' : unicode(etype.__name__),
110 u'ename' : unicode(etype.__name__),
189 u'evalue' : unicode(evalue)
111 u'evalue' : unicode(evalue)
190 }
112 }
191 exc_msg = self.session.msg(u'pyerr', exc_content, parent)
113 exc_msg = self.session.msg(u'pyerr', exc_content, parent)
192 self.pub_socket.send_json(exc_msg)
114 self.pub_socket.send_json(exc_msg)
193 reply_content = exc_content
115 reply_content = exc_content
194 else:
116 else:
195 reply_content = { 'status' : 'ok', 'payload' : self._exec_payload }
117 reply_content = { 'status' : 'ok', 'payload' : {} }
196
118
197 # Flush output before sending the reply.
119 # Flush output before sending the reply.
198 sys.stderr.flush()
120 sys.stderr.flush()
199 sys.stdout.flush()
121 sys.stdout.flush()
200
122
201 # Send the reply.
123 # Send the reply.
202 reply_msg = self.session.msg(u'execute_reply', reply_content, parent)
124 reply_msg = self.session.msg(u'execute_reply', reply_content, parent)
203 print>>sys.__stdout__, Message(reply_msg)
125 print>>sys.__stdout__, Message(reply_msg)
204 self.reply_socket.send(ident, zmq.SNDMORE)
126 self.reply_socket.send(ident, zmq.SNDMORE)
205 self.reply_socket.send_json(reply_msg)
127 self.reply_socket.send_json(reply_msg)
206 if reply_msg['content']['status'] == u'error':
128 if reply_msg['content']['status'] == u'error':
207 self._abort_queue()
129 self._abort_queue()
208
130
209 def complete_request(self, ident, parent):
131 def complete_request(self, ident, parent):
210 comp = self.completer.complete(parent.content.line, parent.content.text)
132 matches = {'matches' : self.complete(parent),
211 matches = {'matches' : comp, 'status' : 'ok'}
133 'status' : 'ok'}
212 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
134 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
213 matches, parent, ident)
135 matches, parent, ident)
214 print >> sys.__stdout__, completion_msg
136 print >> sys.__stdout__, completion_msg
215
137
216 def object_info_request(self, ident, parent):
138 def object_info_request(self, ident, parent):
217 context = parent['content']['oname'].split('.')
139 context = parent['content']['oname'].split('.')
218 object_info = self._object_info(context)
140 object_info = self._object_info(context)
219 msg = self.session.send(self.reply_socket, 'object_info_reply',
141 msg = self.session.send(self.reply_socket, 'object_info_reply',
220 object_info, parent, ident)
142 object_info, parent, ident)
221 print >> sys.__stdout__, msg
143 print >> sys.__stdout__, msg
222
144
223 #---------------------------------------------------------------------------
145 #---------------------------------------------------------------------------
224 # Protected interface
146 # Protected interface
225 #---------------------------------------------------------------------------
147 #---------------------------------------------------------------------------
226
148
227 def _abort_queue(self):
149 def _abort_queue(self):
228 while True:
150 while True:
229 try:
151 try:
230 ident = self.reply_socket.recv(zmq.NOBLOCK)
152 ident = self.reply_socket.recv(zmq.NOBLOCK)
231 except zmq.ZMQError, e:
153 except zmq.ZMQError, e:
232 if e.errno == zmq.EAGAIN:
154 if e.errno == zmq.EAGAIN:
233 break
155 break
234 else:
156 else:
235 assert self.reply_socket.rcvmore(), "Missing message part."
157 assert self.reply_socket.rcvmore(), "Missing message part."
236 msg = self.reply_socket.recv_json()
158 msg = self.reply_socket.recv_json()
237 print>>sys.__stdout__, "Aborting:"
159 print>>sys.__stdout__, "Aborting:"
238 print>>sys.__stdout__, Message(msg)
160 print>>sys.__stdout__, Message(msg)
239 msg_type = msg['msg_type']
161 msg_type = msg['msg_type']
240 reply_type = msg_type.split('_')[0] + '_reply'
162 reply_type = msg_type.split('_')[0] + '_reply'
241 reply_msg = self.session.msg(reply_type, {'status':'aborted'}, msg)
163 reply_msg = self.session.msg(reply_type, {'status':'aborted'}, msg)
242 print>>sys.__stdout__, Message(reply_msg)
164 print>>sys.__stdout__, Message(reply_msg)
243 self.reply_socket.send(ident,zmq.SNDMORE)
165 self.reply_socket.send(ident,zmq.SNDMORE)
244 self.reply_socket.send_json(reply_msg)
166 self.reply_socket.send_json(reply_msg)
245 # We need to wait a bit for requests to come in. This can probably
167 # We need to wait a bit for requests to come in. This can probably
246 # be set shorter for true asynchronous clients.
168 # be set shorter for true asynchronous clients.
247 time.sleep(0.1)
169 time.sleep(0.1)
248
170
249 def _raw_input(self, prompt, ident, parent):
171 def _raw_input(self, prompt, ident, parent):
250 # Flush output before making the request.
172 # Flush output before making the request.
251 sys.stderr.flush()
173 sys.stderr.flush()
252 sys.stdout.flush()
174 sys.stdout.flush()
253
175
254 # Send the input request.
176 # Send the input request.
255 content = dict(prompt=prompt)
177 content = dict(prompt=prompt)
256 msg = self.session.msg(u'input_request', content, parent)
178 msg = self.session.msg(u'input_request', content, parent)
257 self.req_socket.send_json(msg)
179 self.req_socket.send_json(msg)
258
180
259 # Await a response.
181 # Await a response.
260 reply = self.req_socket.recv_json()
182 reply = self.req_socket.recv_json()
261 try:
183 try:
262 value = reply['content']['value']
184 value = reply['content']['value']
263 except:
185 except:
264 print>>sys.__stderr__, "Got bad raw_input reply: "
186 print>>sys.__stderr__, "Got bad raw_input reply: "
265 print>>sys.__stderr__, Message(parent)
187 print>>sys.__stderr__, Message(parent)
266 value = ''
188 value = ''
267 return value
189 return value
268
190
191 def _complete(self, msg):
192 return self.completer.complete(msg.content.line, msg.content.text)
193
269 def _object_info(self, context):
194 def _object_info(self, context):
270 symbol, leftover = self._symbol_from_context(context)
195 symbol, leftover = self._symbol_from_context(context)
271 if symbol is not None and not leftover:
196 if symbol is not None and not leftover:
272 doc = getattr(symbol, '__doc__', '')
197 doc = getattr(symbol, '__doc__', '')
273 else:
198 else:
274 doc = ''
199 doc = ''
275 object_info = dict(docstring = doc)
200 object_info = dict(docstring = doc)
276 return object_info
201 return object_info
277
202
278 def _symbol_from_context(self, context):
203 def _symbol_from_context(self, context):
279 if not context:
204 if not context:
280 return None, context
205 return None, context
281
206
282 base_symbol_string = context[0]
207 base_symbol_string = context[0]
283 symbol = self.user_ns.get(base_symbol_string, None)
208 symbol = self.user_ns.get(base_symbol_string, None)
284 if symbol is None:
209 if symbol is None:
285 symbol = __builtin__.__dict__.get(base_symbol_string, None)
210 symbol = __builtin__.__dict__.get(base_symbol_string, None)
286 if symbol is None:
211 if symbol is None:
287 return None, context
212 return None, context
288
213
289 context = context[1:]
214 context = context[1:]
290 for i, name in enumerate(context):
215 for i, name in enumerate(context):
291 new_symbol = getattr(symbol, name, None)
216 new_symbol = getattr(symbol, name, None)
292 if new_symbol is None:
217 if new_symbol is None:
293 return symbol, context[i:]
218 return symbol, context[i:]
294 else:
219 else:
295 symbol = new_symbol
220 symbol = new_symbol
296
221
297 return symbol, []
222 return symbol, []
298
223
299 #-----------------------------------------------------------------------------
224 #-----------------------------------------------------------------------------
300 # Kernel main and launch functions
225 # Kernel main and launch functions
301 #-----------------------------------------------------------------------------
226 #-----------------------------------------------------------------------------
302
227
303 def bind_port(socket, ip, port):
228 def launch_kernel(xrep_port=0, pub_port=0, req_port=0, independent=False):
304 """ Binds the specified ZMQ socket. If the port is zero, a random port is
305 chosen. Returns the port that was bound.
306 """
307 connection = 'tcp://%s' % ip
308 if port <= 0:
309 port = socket.bind_to_random_port(connection)
310 else:
311 connection += ':%i' % port
312 socket.bind(connection)
313 return port
314
315
316 def main():
317 """ Main entry point for launching a kernel.
318 """
319 # Parse command line arguments.
320 parser = ArgumentParser()
321 parser.add_argument('--ip', type=str, default='127.0.0.1',
322 help='set the kernel\'s IP address [default: local]')
323 parser.add_argument('--xrep', type=int, metavar='PORT', default=0,
324 help='set the XREP channel port [default: random]')
325 parser.add_argument('--pub', type=int, metavar='PORT', default=0,
326 help='set the PUB channel port [default: random]')
327 parser.add_argument('--req', type=int, metavar='PORT', default=0,
328 help='set the REQ channel port [default: random]')
329 if sys.platform == 'win32':
330 parser.add_argument('--parent', type=int, metavar='HANDLE',
331 default=0, help='kill this process if the process '
332 'with HANDLE dies')
333 else:
334 parser.add_argument('--parent', action='store_true',
335 help='kill this process if its parent dies')
336 parser.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
337 const='auto', help = \
338 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
339 given, the GUI backend is matplotlib's, otherwise use one of: \
340 ['tk', 'gtk', 'qt', 'wx', 'payload-svg'].")
341
342 namespace = parser.parse_args()
343
344 # Create a context, a session, and the kernel sockets.
345 print >>sys.__stdout__, "Starting the kernel..."
346 context = zmq.Context()
347 session = Session(username=u'kernel')
348
349 reply_socket = context.socket(zmq.XREP)
350 xrep_port = bind_port(reply_socket, namespace.ip, namespace.xrep)
351 print >>sys.__stdout__, "XREP Channel on port", xrep_port
352
353 pub_socket = context.socket(zmq.PUB)
354 pub_port = bind_port(pub_socket, namespace.ip, namespace.pub)
355 print >>sys.__stdout__, "PUB Channel on port", pub_port
356
357 req_socket = context.socket(zmq.XREQ)
358 req_port = bind_port(req_socket, namespace.ip, namespace.req)
359 print >>sys.__stdout__, "REQ Channel on port", req_port
360
361 # Create the kernel.
362 kernel = Kernel(session, reply_socket, pub_socket, req_socket)
363
364 # Set up pylab, if necessary.
365 if namespace.pylab:
366 if namespace.pylab == 'auto':
367 kernel.activate_pylab()
368 else:
369 kernel.activate_pylab(namespace.pylab)
370
371 # Redirect input streams and set a display hook.
372 sys.stdout = OutStream(session, pub_socket, u'stdout')
373 sys.stderr = OutStream(session, pub_socket, u'stderr')
374 sys.displayhook = DisplayHook(session, pub_socket)
375
376 # Configure this kernel/process to die on parent termination, if necessary.
377 if namespace.parent:
378 if sys.platform == 'win32':
379 poller = ExitPollerWindows(namespace.parent)
380 else:
381 poller = ExitPollerUnix()
382 poller.start()
383
384 # Start the kernel mainloop.
385 kernel.start()
386
387
388 def launch_kernel(xrep_port=0, pub_port=0, req_port=0,
389 pylab=False, independent=False):
390 """ Launches a localhost kernel, binding to the specified ports.
229 """ Launches a localhost kernel, binding to the specified ports.
391
230
392 Parameters
231 Parameters
393 ----------
232 ----------
394 xrep_port : int, optional
233 xrep_port : int, optional
395 The port to use for XREP channel.
234 The port to use for XREP channel.
396
235
397 pub_port : int, optional
236 pub_port : int, optional
398 The port to use for the SUB channel.
237 The port to use for the SUB channel.
399
238
400 req_port : int, optional
239 req_port : int, optional
401 The port to use for the REQ (raw input) channel.
240 The port to use for the REQ (raw input) channel.
402
241
403 pylab : bool or string, optional (default False)
404 If not False, the kernel will be launched with pylab enabled. If a
405 string is passed, matplotlib will use the specified backend. Otherwise,
406 matplotlib's default backend will be used.
407
408 independent : bool, optional (default False)
242 independent : bool, optional (default False)
409 If set, the kernel process is guaranteed to survive if this process
243 If set, the kernel process is guaranteed to survive if this process
410 dies. If not set, an effort is made to ensure that the kernel is killed
244 dies. If not set, an effort is made to ensure that the kernel is killed
411 when this process dies. Note that in this case it is still good practice
245 when this process dies. Note that in this case it is still good practice
412 to kill kernels manually before exiting.
246 to kill kernels manually before exiting.
413
247
414 Returns
248 Returns
415 -------
249 -------
416 A tuple of form:
250 A tuple of form:
417 (kernel_process, xrep_port, pub_port, req_port)
251 (kernel_process, xrep_port, pub_port, req_port)
418 where kernel_process is a Popen object and the ports are integers.
252 where kernel_process is a Popen object and the ports are integers.
419 """
253 """
420 import socket
254 return base_launch_kernel('from IPython.zmq.pykernel import main; main()',
421 from subprocess import Popen
255 xrep_port, pub_port, req_port, independent)
422
423 # Find open ports as necessary.
424 ports = []
425 ports_needed = int(xrep_port <= 0) + int(pub_port <= 0) + int(req_port <= 0)
426 for i in xrange(ports_needed):
427 sock = socket.socket()
428 sock.bind(('', 0))
429 ports.append(sock)
430 for i, sock in enumerate(ports):
431 port = sock.getsockname()[1]
432 sock.close()
433 ports[i] = port
434 if xrep_port <= 0:
435 xrep_port = ports.pop(0)
436 if pub_port <= 0:
437 pub_port = ports.pop(0)
438 if req_port <= 0:
439 req_port = ports.pop(0)
440
441 # Build the kernel launch command.
442 command = 'from IPython.zmq.pykernel import main; main()'
443 arguments = [ sys.executable, '-c', command, '--xrep', str(xrep_port),
444 '--pub', str(pub_port), '--req', str(req_port) ]
445 if pylab:
446 arguments.append('--pylab')
447 if isinstance(pylab, basestring):
448 arguments.append(pylab)
449
450 # Spawn a kernel.
451 if independent:
452 if sys.platform == 'win32':
453 proc = Popen(['start', '/b'] + arguments, shell=True)
454 else:
455 proc = Popen(arguments, preexec_fn=lambda: os.setsid())
456 else:
457 if sys.platform == 'win32':
458 from _subprocess import DuplicateHandle, GetCurrentProcess, \
459 DUPLICATE_SAME_ACCESS
460 pid = GetCurrentProcess()
461 handle = DuplicateHandle(pid, pid, pid, 0,
462 True, # Inheritable by new processes.
463 DUPLICATE_SAME_ACCESS)
464 proc = Popen(arguments + ['--parent', str(int(handle))])
465 else:
466 proc = Popen(arguments + ['--parent'])
467
256
468 return proc, xrep_port, pub_port, req_port
257 main = make_default_main(Kernel)
469
470
258
471 if __name__ == '__main__':
259 if __name__ == '__main__':
472 main()
260 main()
@@ -1,23 +1,23 b''
1 """ Provides basic funtionality for payload backends.
1 """ Provides basic funtionality for payload backends.
2 """
2 """
3
3
4 # Local imports.
4 # Local imports.
5 from IPython.zmq.kernel import Kernel
5 from IPython.zmq.ipkernel import Kernel
6
6
7
7
8 def add_plot_payload(format, data, metadata={}):
8 def add_plot_payload(format, data, metadata={}):
9 """ Add a plot payload to the current execution reply.
9 """ Add a plot payload to the current execution reply.
10
10
11 Parameters:
11 Parameters:
12 -----------
12 -----------
13 format : str
13 format : str
14 Identifies the format of the plot data.
14 Identifies the format of the plot data.
15
15
16 data : str
16 data : str
17 The raw plot data.
17 The raw plot data.
18
18
19 metadata : dict, optional [default empty]
19 metadata : dict, optional [default empty]
20 Allows for specification of additional information about the plot data.
20 Allows for specification of additional information about the plot data.
21 """
21 """
22 payload = dict(format=format, data=data, metadata=metadata)
22 payload = dict(format=format, data=data, metadata=metadata)
23 Kernel.get_kernel().add_exec_payload('plot', payload)
23 Kernel.get_kernel().add_exec_payload('plot', payload)
General Comments 0
You need to be logged in to leave comments. Login now