##// 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
@@ -7,8 +7,6 b' from PyQt4 import QtCore, QtGui'
7 7 # Local imports
8 8 from IPython.external.argparse import ArgumentParser
9 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 12 def main():
@@ -16,10 +14,13 b' def main():'
16 14 """
17 15 # Parse command line arguments.
18 16 parser = ArgumentParser()
19 parser.add_argument('--pylab', action='store_true',
20 help='start kernel with pylab enabled')
17 group = parser.add_mutually_exclusive_group()
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 22 parser.add_argument('--rich', action='store_true',
22 help='use rich text frontend')
23 help='use a rich text frontend')
23 24 namespace = parser.parse_args()
24 25
25 26 # Don't let Qt or ZMQ swallow KeyboardInterupts.
@@ -28,7 +29,9 b' def main():'
28 29
29 30 # Create a KernelManager and start a kernel.
30 31 kernel_manager = QtKernelManager()
31 if namespace.pylab:
32 if namespace.pure:
33 kernel_manager.start_kernel(ipython=False)
34 elif namespace.pylab:
32 35 if namespace.rich:
33 36 kernel_manager.start_kernel(pylab='payload-svg')
34 37 else:
@@ -39,10 +42,17 b' def main():'
39 42
40 43 # Launch the application.
41 44 app = QtGui.QApplication([])
42 if namespace.rich:
43 widget = RichIPythonWidget()
45 if namespace.pure:
46 from frontend_widget import FrontendWidget
47 kind = 'rich' if namespace.rich else 'plain'
48 widget = FrontendWidget(kind=kind)
44 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 56 widget.kernel_manager = kernel_manager
47 57 widget.setWindowTitle('Python')
48 58 widget.show()
@@ -17,7 +17,6 b' Things to do:'
17 17 # Standard library imports.
18 18 import __builtin__
19 19 from code import CommandCompiler
20 import os
21 20 import sys
22 21 import time
23 22 import traceback
@@ -27,14 +26,12 b' import zmq'
27 26
28 27 # Local imports.
29 28 from IPython.config.configurable import Configurable
30 from IPython.zmq.zmqshell import ZMQInteractiveShell
31 from IPython.external.argparse import ArgumentParser
32 29 from IPython.utils.traitlets import Instance
33 from IPython.zmq.session import Session, Message
34 30 from completer import KernelCompleter
35 from iostream import OutStream
36 from displayhook import DisplayHook
37 from exitpoller import ExitPollerUnix, ExitPollerWindows
31 from entry_point import base_launch_kernel, make_argument_parser, make_kernel, \
32 start_kernel
33 from session import Session, Message
34 from zmqshell import ZMQInteractiveShell
38 35
39 36 #-----------------------------------------------------------------------------
40 37 # Main kernel class
@@ -42,15 +39,34 b' from exitpoller import ExitPollerUnix, ExitPollerWindows'
42 39
43 40 class Kernel(Configurable):
44 41
42 #---------------------------------------------------------------------------
43 # Kernel interface
44 #---------------------------------------------------------------------------
45
45 46 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
46 session = Instance('IPython.zmq.session.Session')
47 session = Instance(Session)
47 48 reply_socket = Instance('zmq.Socket')
48 49 pub_socket = Instance('zmq.Socket')
49 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 64 def __init__(self, **kwargs):
52 65 super(Kernel, self).__init__(**kwargs)
53 66 self.shell = ZMQInteractiveShell.instance()
67
68 # Protected variables.
69 self._exec_payload = {}
54 70
55 71 # Build dict of handlers for message types
56 72 msg_types = [ 'execute_request', 'complete_request',
@@ -59,27 +75,84 b' class Kernel(Configurable):'
59 75 for msg_type in msg_types:
60 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 140 while True:
64 try:
65 ident = self.reply_socket.recv(zmq.NOBLOCK)
66 except zmq.ZMQError, e:
67 if e.errno == zmq.EAGAIN:
68 break
141 ident = self.reply_socket.recv()
142 assert self.reply_socket.rcvmore(), "Missing message part."
143 msg = self.reply_socket.recv_json()
144 omsg = Message(msg)
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 150 else:
70 assert self.reply_socket.rcvmore(), "Unexpected missing message part."
71 msg = self.reply_socket.recv_json()
72 print>>sys.__stdout__, "Aborting:"
73 print>>sys.__stdout__, Message(msg)
74 msg_type = msg['msg_type']
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)
151 handler(ident, omsg)
152
153 #---------------------------------------------------------------------------
154 # Kernel request handlers
155 #---------------------------------------------------------------------------
83 156
84 157 def execute_request(self, ident, parent):
85 158 try:
@@ -91,10 +164,13 b' class Kernel(Configurable):'
91 164 pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
92 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 170 try:
95 171 # Replace raw_input. Note that is not sufficient to replace
96 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 174 __builtin__.raw_input = raw_input
99 175
100 176 # Configure the display hook.
@@ -115,7 +191,7 b' class Kernel(Configurable):'
115 191 self.pub_socket.send_json(exc_msg)
116 192 reply_content = exc_content
117 193 else:
118 reply_content = {'status' : 'ok'}
194 reply_content = { 'status' : 'ok', 'payload' : self._exec_payload }
119 195
120 196 # Flush output before sending the reply.
121 197 sys.stderr.flush()
@@ -127,9 +203,49 b' class Kernel(Configurable):'
127 203 self.reply_socket.send(ident, zmq.SNDMORE)
128 204 self.reply_socket.send_json(reply_msg)
129 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 249 # Flush output before making the request.
134 250 sys.stderr.flush()
135 251 sys.stdout.flush()
@@ -148,26 +264,12 b' class Kernel(Configurable):'
148 264 print>>sys.__stderr__, Message(parent)
149 265 value = ''
150 266 return value
151
152 def complete_request(self, ident, parent):
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):
267
268 def _complete(self, msg):
160 269 return self.shell.complete(msg.content.line)
161 270
162 def object_info_request(self, ident, parent):
163 context = parent['content']['oname'].split('.')
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)
271 def _object_info(self, context):
272 symbol, leftover = self._symbol_from_context(context)
171 273 if symbol is not None and not leftover:
172 274 doc = getattr(symbol, '__doc__', '')
173 275 else:
@@ -175,7 +277,7 b' class Kernel(Configurable):'
175 277 object_info = dict(docstring = doc)
176 278 return object_info
177 279
178 def symbol_from_context(self, context):
280 def _symbol_from_context(self, context):
179 281 if not context:
180 282 return None, context
181 283
@@ -196,103 +298,12 b' class Kernel(Configurable):'
196 298
197 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 302 # Kernel main and launch functions
215 303 #-----------------------------------------------------------------------------
216 304
217 def bind_port(socket, ip, port):
218 """ Binds the specified ZMQ socket. If the port is less than zero, a random
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):
305 def launch_kernel(xrep_port=0, pub_port=0, req_port=0, independent=False,
306 pylab=False):
296 307 """ Launches a localhost kernel, binding to the specified ports.
297 308
298 309 Parameters
@@ -312,56 +323,45 b' def launch_kernel(xrep_port=0, pub_port=0, req_port=0, independent=False):'
312 323 when this process dies. Note that in this case it is still good practice
313 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 331 Returns
316 332 -------
317 333 A tuple of form:
318 334 (kernel_process, xrep_port, pub_port, req_port)
319 335 where kernel_process is a Popen object and the ports are integers.
320 336 """
321 import socket
322 from subprocess import Popen
323
324 # Find open ports as necessary.
325 ports = []
326 ports_needed = int(xrep_port <= 0) + int(pub_port <= 0) + int(req_port <= 0)
327 for i in xrange(ports_needed):
328 sock = socket.socket()
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'])
337 extra_arguments = []
338 if pylab:
339 extra_arguments.append('--pylab')
340 if isinstance(pylab, basestring):
341 extra_arguments.append(pylab)
342 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
343 xrep_port, pub_port, req_port, independent,
344 extra_arguments)
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 366 if __name__ == '__main__':
367 367 main()
@@ -30,7 +30,6 b' from zmq.eventloop import ioloop'
30 30
31 31 # Local imports.
32 32 from IPython.utils.traitlets import HasTraits, Any, Instance, Type, TCPAddress
33 from ipkernel import launch_kernel
34 33 from session import Session
35 34
36 35 #-----------------------------------------------------------------------------
@@ -485,7 +484,7 b' class KernelManager(HasTraits):'
485 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 488 """Starts a kernel process and configures the manager to use it.
490 489
491 490 If random ports (port=0) are being used, this method must be called
@@ -493,8 +492,8 b' class KernelManager(HasTraits):'
493 492
494 493 Parameters:
495 494 -----------
496 pylab : bool or string, optional (default False)
497 See IPython.zmq.kernel.launch_kernel for documentation.
495 ipython : bool, optional (default True)
496 Whether to use an IPython kernel instead of a plain Python kernel.
498 497 """
499 498 xreq, sub, rep = self.xreq_address, self.sub_address, self.rep_address
500 499 if xreq[0] != LOCALHOST or sub[0] != LOCALHOST or rep[0] != LOCALHOST:
@@ -502,8 +501,12 b' class KernelManager(HasTraits):'
502 501 "Make sure that the '*_address' attributes are "
503 502 "configured properly.")
504 503
505 self.kernel, xrep, pub, req = launch_kernel(
506 xrep_port=xreq[1], pub_port=sub[1], req_port=rep[1], pylab=pylab)
504 if ipython:
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 510 self.xreq_address = (LOCALHOST, xrep)
508 511 self.sub_address = (LOCALHOST, pub)
509 512 self.rep_address = (LOCALHOST, req)
@@ -17,7 +17,6 b' Things to do:'
17 17 # Standard library imports.
18 18 import __builtin__
19 19 from code import CommandCompiler
20 import os
21 20 import sys
22 21 import time
23 22 import traceback
@@ -26,48 +25,33 b' import traceback'
26 25 import zmq
27 26
28 27 # Local imports.
29 from IPython.external.argparse import ArgumentParser
30 from session import Session, Message
28 from IPython.utils.traitlets import HasTraits, Instance
31 29 from completer import KernelCompleter
32 from iostream import OutStream
33 from displayhook import DisplayHook
34 from exitpoller import ExitPollerUnix, ExitPollerWindows
30 from entry_point import base_launch_kernel, make_default_main
31 from session import Session, Message
35 32
36 33 #-----------------------------------------------------------------------------
37 34 # Main kernel class
38 35 #-----------------------------------------------------------------------------
39 36
40 class Kernel(object):
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' }
37 class Kernel(HasTraits):
53 38
54 39 #---------------------------------------------------------------------------
55 40 # Kernel interface
56 41 #---------------------------------------------------------------------------
57 42
58 def __init__(self, session, reply_socket, pub_socket, req_socket):
59 self.session = session
60 self.reply_socket = reply_socket
61 self.pub_socket = pub_socket
62 self.req_socket = req_socket
43 session = Instance(Session)
44 reply_socket = Instance('zmq.Socket')
45 pub_socket = Instance('zmq.Socket')
46 req_socket = Instance('zmq.Socket')
47
48 def __init__(self, **kwargs):
49 super(Kernel, self).__init__(**kwargs)
63 50 self.user_ns = {}
64 51 self.history = []
65 52 self.compiler = CommandCompiler()
66 53 self.completer = KernelCompleter(self.user_ns)
67 54
68 # Protected variables.
69 self._exec_payload = {}
70
71 55 # Build dict of handlers for message types
72 56 msg_types = [ 'execute_request', 'complete_request',
73 57 'object_info_request' ]
@@ -75,68 +59,9 b' class Kernel(object):'
75 59 for msg_type in msg_types:
76 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 62 def start(self):
135 63 """ Start the kernel main loop.
136 64 """
137 # Set the global kernel instance.
138 Kernel._kernel = self
139
140 65 while True:
141 66 ident = self.reply_socket.recv()
142 67 assert self.reply_socket.rcvmore(), "Missing message part."
@@ -164,9 +89,6 b' class Kernel(object):'
164 89 pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
165 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 92 try:
171 93 comp_code = self.compiler(code, '<zmq-kernel>')
172 94
@@ -192,7 +114,7 b' class Kernel(object):'
192 114 self.pub_socket.send_json(exc_msg)
193 115 reply_content = exc_content
194 116 else:
195 reply_content = { 'status' : 'ok', 'payload' : self._exec_payload }
117 reply_content = { 'status' : 'ok', 'payload' : {} }
196 118
197 119 # Flush output before sending the reply.
198 120 sys.stderr.flush()
@@ -207,8 +129,8 b' class Kernel(object):'
207 129 self._abort_queue()
208 130
209 131 def complete_request(self, ident, parent):
210 comp = self.completer.complete(parent.content.line, parent.content.text)
211 matches = {'matches' : comp, 'status' : 'ok'}
132 matches = {'matches' : self.complete(parent),
133 'status' : 'ok'}
212 134 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
213 135 matches, parent, ident)
214 136 print >> sys.__stdout__, completion_msg
@@ -266,6 +188,9 b' class Kernel(object):'
266 188 value = ''
267 189 return value
268 190
191 def _complete(self, msg):
192 return self.completer.complete(msg.content.line, msg.content.text)
193
269 194 def _object_info(self, context):
270 195 symbol, leftover = self._symbol_from_context(context)
271 196 if symbol is not None and not leftover:
@@ -300,93 +225,7 b' class Kernel(object):'
300 225 # Kernel main and launch functions
301 226 #-----------------------------------------------------------------------------
302 227
303 def bind_port(socket, ip, port):
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):
228 def launch_kernel(xrep_port=0, pub_port=0, req_port=0, independent=False):
390 229 """ Launches a localhost kernel, binding to the specified ports.
391 230
392 231 Parameters
@@ -400,11 +239,6 b' def launch_kernel(xrep_port=0, pub_port=0, req_port=0,'
400 239 req_port : int, optional
401 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 242 independent : bool, optional (default False)
409 243 If set, the kernel process is guaranteed to survive if this process
410 244 dies. If not set, an effort is made to ensure that the kernel is killed
@@ -417,56 +251,10 b' def launch_kernel(xrep_port=0, pub_port=0, req_port=0,'
417 251 (kernel_process, xrep_port, pub_port, req_port)
418 252 where kernel_process is a Popen object and the ports are integers.
419 253 """
420 import socket
421 from subprocess import Popen
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'])
254 return base_launch_kernel('from IPython.zmq.pykernel import main; main()',
255 xrep_port, pub_port, req_port, independent)
467 256
468 return proc, xrep_port, pub_port, req_port
469
257 main = make_default_main(Kernel)
470 258
471 259 if __name__ == '__main__':
472 260 main()
@@ -2,7 +2,7 b''
2 2 """
3 3
4 4 # Local imports.
5 from IPython.zmq.kernel import Kernel
5 from IPython.zmq.ipkernel import Kernel
6 6
7 7
8 8 def add_plot_payload(format, data, metadata={}):
General Comments 0
You need to be logged in to leave comments. Login now