##// 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 # 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():
@@ -16,10 +14,13 b' def main():'
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.
@@ -28,7 +29,9 b' def main():'
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:
@@ -39,10 +42,17 b' def main():'
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()
@@ -17,7 +17,6 b' Things to do:'
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
@@ -27,14 +26,12 b' 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
@@ -42,15 +39,34 b' from exitpoller import ExitPollerUnix, ExitPollerWindows'
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',
@@ -59,27 +75,84 b' class Kernel(Configurable):'
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:
@@ -91,10 +164,13 b' class Kernel(Configurable):'
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.
@@ -115,7 +191,7 b' class Kernel(Configurable):'
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()
@@ -127,9 +203,49 b' class Kernel(Configurable):'
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()
@@ -148,26 +264,12 b' class Kernel(Configurable):'
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:
@@ -175,7 +277,7 b' class Kernel(Configurable):'
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
@@ -196,103 +298,12 b' class Kernel(Configurable):'
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
@@ -312,56 +323,45 b' def launch_kernel(xrep_port=0, pub_port=0, req_port=0, independent=False):'
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()
@@ -30,7 +30,6 b' 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 #-----------------------------------------------------------------------------
@@ -485,7 +484,7 b' class KernelManager(HasTraits):'
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
@@ -493,8 +492,8 b' class KernelManager(HasTraits):'
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:
@@ -502,8 +501,12 b' class KernelManager(HasTraits):'
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)
@@ -17,7 +17,6 b' Things to do:'
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
@@ -26,48 +25,33 b' import traceback'
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' ]
@@ -75,68 +59,9 b' class Kernel(object):'
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."
@@ -164,9 +89,6 b' class Kernel(object):'
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
@@ -192,7 +114,7 b' class Kernel(object):'
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()
@@ -207,8 +129,8 b' class Kernel(object):'
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
@@ -266,6 +188,9 b' class Kernel(object):'
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:
@@ -300,93 +225,7 b' class Kernel(object):'
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
@@ -400,11 +239,6 b' def launch_kernel(xrep_port=0, pub_port=0, req_port=0,'
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
@@ -417,56 +251,10 b' def launch_kernel(xrep_port=0, pub_port=0, req_port=0,'
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()
@@ -2,7 +2,7 b''
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={}):
General Comments 0
You need to be logged in to leave comments. Login now