##// END OF EJS Templates
Add option for specifying Python executable to 'launch_kernel'.
epatters -
Show More
@@ -1,260 +1,265
1 """ Defines helper functions for creating kernel entry points and process
1 """ Defines helper functions for creating kernel entry points and process
2 launchers.
2 launchers.
3 """
3 """
4
4
5 # Standard library imports.
5 # Standard library imports.
6 import atexit
6 import atexit
7 import os
7 import os
8 import socket
8 import socket
9 from subprocess import Popen, PIPE
9 from subprocess import Popen, PIPE
10 import sys
10 import sys
11
11
12 # System library imports.
12 # System library imports.
13 import zmq
13 import zmq
14
14
15 # Local imports.
15 # Local imports.
16 from IPython.core.ultratb import FormattedTB
16 from IPython.core.ultratb import FormattedTB
17 from IPython.external.argparse import ArgumentParser
17 from IPython.external.argparse import ArgumentParser
18 from IPython.utils import io
18 from IPython.utils import io
19 from IPython.utils.localinterfaces import LOCALHOST
19 from IPython.utils.localinterfaces import LOCALHOST
20 from displayhook import DisplayHook
20 from displayhook import DisplayHook
21 from heartbeat import Heartbeat
21 from heartbeat import Heartbeat
22 from iostream import OutStream
22 from iostream import OutStream
23 from parentpoller import ParentPollerUnix, ParentPollerWindows
23 from parentpoller import ParentPollerUnix, ParentPollerWindows
24 from session import Session
24 from session import Session
25
25
26 def bind_port(socket, ip, port):
26 def bind_port(socket, ip, port):
27 """ Binds the specified ZMQ socket. If the port is zero, a random port is
27 """ Binds the specified ZMQ socket. If the port is zero, a random port is
28 chosen. Returns the port that was bound.
28 chosen. Returns the port that was bound.
29 """
29 """
30 connection = 'tcp://%s' % ip
30 connection = 'tcp://%s' % ip
31 if port <= 0:
31 if port <= 0:
32 port = socket.bind_to_random_port(connection)
32 port = socket.bind_to_random_port(connection)
33 else:
33 else:
34 connection += ':%i' % port
34 connection += ':%i' % port
35 socket.bind(connection)
35 socket.bind(connection)
36 return port
36 return port
37
37
38
38
39 def make_argument_parser():
39 def make_argument_parser():
40 """ Creates an ArgumentParser for the generic arguments supported by all
40 """ Creates an ArgumentParser for the generic arguments supported by all
41 kernel entry points.
41 kernel entry points.
42 """
42 """
43 parser = ArgumentParser()
43 parser = ArgumentParser()
44 parser.add_argument('--ip', type=str, default=LOCALHOST,
44 parser.add_argument('--ip', type=str, default=LOCALHOST,
45 help='set the kernel\'s IP address [default: local]')
45 help='set the kernel\'s IP address [default: local]')
46 parser.add_argument('--xrep', type=int, metavar='PORT', default=0,
46 parser.add_argument('--xrep', type=int, metavar='PORT', default=0,
47 help='set the XREP channel port [default: random]')
47 help='set the XREP channel port [default: random]')
48 parser.add_argument('--pub', type=int, metavar='PORT', default=0,
48 parser.add_argument('--pub', type=int, metavar='PORT', default=0,
49 help='set the PUB channel port [default: random]')
49 help='set the PUB channel port [default: random]')
50 parser.add_argument('--req', type=int, metavar='PORT', default=0,
50 parser.add_argument('--req', type=int, metavar='PORT', default=0,
51 help='set the REQ channel port [default: random]')
51 help='set the REQ channel port [default: random]')
52 parser.add_argument('--hb', type=int, metavar='PORT', default=0,
52 parser.add_argument('--hb', type=int, metavar='PORT', default=0,
53 help='set the heartbeat port [default: random]')
53 help='set the heartbeat port [default: random]')
54
54
55 if sys.platform == 'win32':
55 if sys.platform == 'win32':
56 parser.add_argument('--interrupt', type=int, metavar='HANDLE',
56 parser.add_argument('--interrupt', type=int, metavar='HANDLE',
57 default=0, help='interrupt this process when '
57 default=0, help='interrupt this process when '
58 'HANDLE is signaled')
58 'HANDLE is signaled')
59 parser.add_argument('--parent', type=int, metavar='HANDLE',
59 parser.add_argument('--parent', type=int, metavar='HANDLE',
60 default=0, help='kill this process if the process '
60 default=0, help='kill this process if the process '
61 'with HANDLE dies')
61 'with HANDLE dies')
62 else:
62 else:
63 parser.add_argument('--parent', action='store_true',
63 parser.add_argument('--parent', action='store_true',
64 help='kill this process if its parent dies')
64 help='kill this process if its parent dies')
65
65
66 return parser
66 return parser
67
67
68
68
69 def make_kernel(namespace, kernel_factory,
69 def make_kernel(namespace, kernel_factory,
70 out_stream_factory=None, display_hook_factory=None):
70 out_stream_factory=None, display_hook_factory=None):
71 """ Creates a kernel, redirects stdout/stderr, and installs a display hook
71 """ Creates a kernel, redirects stdout/stderr, and installs a display hook
72 and exception handler.
72 and exception handler.
73 """
73 """
74 # If running under pythonw.exe, the interpreter will crash if more than 4KB
74 # If running under pythonw.exe, the interpreter will crash if more than 4KB
75 # of data is written to stdout or stderr. This is a bug that has been with
75 # of data is written to stdout or stderr. This is a bug that has been with
76 # Python for a very long time; see http://bugs.python.org/issue706263.
76 # Python for a very long time; see http://bugs.python.org/issue706263.
77 if sys.executable.endswith('pythonw.exe'):
77 if sys.executable.endswith('pythonw.exe'):
78 blackhole = file(os.devnull, 'w')
78 blackhole = file(os.devnull, 'w')
79 sys.stdout = sys.stderr = blackhole
79 sys.stdout = sys.stderr = blackhole
80 sys.__stdout__ = sys.__stderr__ = blackhole
80 sys.__stdout__ = sys.__stderr__ = blackhole
81
81
82 # Install minimal exception handling
82 # Install minimal exception handling
83 sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor',
83 sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor',
84 ostream=sys.__stdout__)
84 ostream=sys.__stdout__)
85
85
86 # Create a context, a session, and the kernel sockets.
86 # Create a context, a session, and the kernel sockets.
87 io.raw_print("Starting the kernel at pid:", os.getpid())
87 io.raw_print("Starting the kernel at pid:", os.getpid())
88 context = zmq.Context()
88 context = zmq.Context()
89 # Uncomment this to try closing the context.
89 # Uncomment this to try closing the context.
90 # atexit.register(context.close)
90 # atexit.register(context.close)
91 session = Session(username=u'kernel')
91 session = Session(username=u'kernel')
92
92
93 reply_socket = context.socket(zmq.XREP)
93 reply_socket = context.socket(zmq.XREP)
94 xrep_port = bind_port(reply_socket, namespace.ip, namespace.xrep)
94 xrep_port = bind_port(reply_socket, namespace.ip, namespace.xrep)
95 io.raw_print("XREP Channel on port", xrep_port)
95 io.raw_print("XREP Channel on port", xrep_port)
96
96
97 pub_socket = context.socket(zmq.PUB)
97 pub_socket = context.socket(zmq.PUB)
98 pub_port = bind_port(pub_socket, namespace.ip, namespace.pub)
98 pub_port = bind_port(pub_socket, namespace.ip, namespace.pub)
99 io.raw_print("PUB Channel on port", pub_port)
99 io.raw_print("PUB Channel on port", pub_port)
100
100
101 req_socket = context.socket(zmq.XREQ)
101 req_socket = context.socket(zmq.XREQ)
102 req_port = bind_port(req_socket, namespace.ip, namespace.req)
102 req_port = bind_port(req_socket, namespace.ip, namespace.req)
103 io.raw_print("REQ Channel on port", req_port)
103 io.raw_print("REQ Channel on port", req_port)
104
104
105 hb = Heartbeat(context, (namespace.ip, namespace.hb))
105 hb = Heartbeat(context, (namespace.ip, namespace.hb))
106 hb.start()
106 hb.start()
107 hb_port = hb.port
107 hb_port = hb.port
108 io.raw_print("Heartbeat REP Channel on port", hb_port)
108 io.raw_print("Heartbeat REP Channel on port", hb_port)
109
109
110 # Helper to make it easier to connect to an existing kernel, until we have
110 # Helper to make it easier to connect to an existing kernel, until we have
111 # single-port connection negotiation fully implemented.
111 # single-port connection negotiation fully implemented.
112 io.raw_print("To connect another client to this kernel, use:")
112 io.raw_print("To connect another client to this kernel, use:")
113 io.raw_print("-e --xreq {0} --sub {1} --rep {2} --hb {3}".format(
113 io.raw_print("-e --xreq {0} --sub {1} --rep {2} --hb {3}".format(
114 xrep_port, pub_port, req_port, hb_port))
114 xrep_port, pub_port, req_port, hb_port))
115
115
116 # Redirect input streams and set a display hook.
116 # Redirect input streams and set a display hook.
117 if out_stream_factory:
117 if out_stream_factory:
118 sys.stdout = out_stream_factory(session, pub_socket, u'stdout')
118 sys.stdout = out_stream_factory(session, pub_socket, u'stdout')
119 sys.stderr = out_stream_factory(session, pub_socket, u'stderr')
119 sys.stderr = out_stream_factory(session, pub_socket, u'stderr')
120 if display_hook_factory:
120 if display_hook_factory:
121 sys.displayhook = display_hook_factory(session, pub_socket)
121 sys.displayhook = display_hook_factory(session, pub_socket)
122
122
123 # Create the kernel.
123 # Create the kernel.
124 kernel = kernel_factory(session=session, reply_socket=reply_socket,
124 kernel = kernel_factory(session=session, reply_socket=reply_socket,
125 pub_socket=pub_socket, req_socket=req_socket)
125 pub_socket=pub_socket, req_socket=req_socket)
126 kernel.record_ports(xrep_port=xrep_port, pub_port=pub_port,
126 kernel.record_ports(xrep_port=xrep_port, pub_port=pub_port,
127 req_port=req_port, hb_port=hb_port)
127 req_port=req_port, hb_port=hb_port)
128 return kernel
128 return kernel
129
129
130
130
131 def start_kernel(namespace, kernel):
131 def start_kernel(namespace, kernel):
132 """ Starts a kernel.
132 """ Starts a kernel.
133 """
133 """
134 # Configure this kernel process to poll the parent process, if necessary.
134 # Configure this kernel process to poll the parent process, if necessary.
135 if sys.platform == 'win32':
135 if sys.platform == 'win32':
136 if namespace.interrupt or namespace.parent:
136 if namespace.interrupt or namespace.parent:
137 poller = ParentPollerWindows(namespace.interrupt, namespace.parent)
137 poller = ParentPollerWindows(namespace.interrupt, namespace.parent)
138 poller.start()
138 poller.start()
139 elif namespace.parent:
139 elif namespace.parent:
140 poller = ParentPollerUnix()
140 poller = ParentPollerUnix()
141 poller.start()
141 poller.start()
142
142
143 # Start the kernel mainloop.
143 # Start the kernel mainloop.
144 kernel.start()
144 kernel.start()
145
145
146
146
147 def make_default_main(kernel_factory):
147 def make_default_main(kernel_factory):
148 """ Creates the simplest possible kernel entry point.
148 """ Creates the simplest possible kernel entry point.
149 """
149 """
150 def main():
150 def main():
151 namespace = make_argument_parser().parse_args()
151 namespace = make_argument_parser().parse_args()
152 kernel = make_kernel(namespace, kernel_factory, OutStream, DisplayHook)
152 kernel = make_kernel(namespace, kernel_factory, OutStream, DisplayHook)
153 start_kernel(namespace, kernel)
153 start_kernel(namespace, kernel)
154 return main
154 return main
155
155
156
156
157 def base_launch_kernel(code, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
157 def base_launch_kernel(code, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
158 independent=False, extra_arguments=[]):
158 executable=None, independent=False, extra_arguments=[]):
159 """ Launches a localhost kernel, binding to the specified ports.
159 """ Launches a localhost kernel, binding to the specified ports.
160
160
161 Parameters
161 Parameters
162 ----------
162 ----------
163 code : str,
163 code : str,
164 A string of Python code that imports and executes a kernel entry point.
164 A string of Python code that imports and executes a kernel entry point.
165
165
166 xrep_port : int, optional
166 xrep_port : int, optional
167 The port to use for XREP channel.
167 The port to use for XREP channel.
168
168
169 pub_port : int, optional
169 pub_port : int, optional
170 The port to use for the SUB channel.
170 The port to use for the SUB channel.
171
171
172 req_port : int, optional
172 req_port : int, optional
173 The port to use for the REQ (raw input) channel.
173 The port to use for the REQ (raw input) channel.
174
174
175 hb_port : int, optional
175 hb_port : int, optional
176 The port to use for the hearbeat REP channel.
176 The port to use for the hearbeat REP channel.
177
177
178 executable : str, optional (default sys.executable)
179 The Python executable to use for the kernel process.
180
178 independent : bool, optional (default False)
181 independent : bool, optional (default False)
179 If set, the kernel process is guaranteed to survive if this process
182 If set, the kernel process is guaranteed to survive if this process
180 dies. If not set, an effort is made to ensure that the kernel is killed
183 dies. If not set, an effort is made to ensure that the kernel is killed
181 when this process dies. Note that in this case it is still good practice
184 when this process dies. Note that in this case it is still good practice
182 to kill kernels manually before exiting.
185 to kill kernels manually before exiting.
183
186
184 extra_arguments = list, optional
187 extra_arguments = list, optional
185 A list of extra arguments to pass when executing the launch code.
188 A list of extra arguments to pass when executing the launch code.
186
189
187 Returns
190 Returns
188 -------
191 -------
189 A tuple of form:
192 A tuple of form:
190 (kernel_process, xrep_port, pub_port, req_port)
193 (kernel_process, xrep_port, pub_port, req_port)
191 where kernel_process is a Popen object and the ports are integers.
194 where kernel_process is a Popen object and the ports are integers.
192 """
195 """
193 # Find open ports as necessary.
196 # Find open ports as necessary.
194 ports = []
197 ports = []
195 ports_needed = int(xrep_port <= 0) + int(pub_port <= 0) + \
198 ports_needed = int(xrep_port <= 0) + int(pub_port <= 0) + \
196 int(req_port <= 0) + int(hb_port <= 0)
199 int(req_port <= 0) + int(hb_port <= 0)
197 for i in xrange(ports_needed):
200 for i in xrange(ports_needed):
198 sock = socket.socket()
201 sock = socket.socket()
199 sock.bind(('', 0))
202 sock.bind(('', 0))
200 ports.append(sock)
203 ports.append(sock)
201 for i, sock in enumerate(ports):
204 for i, sock in enumerate(ports):
202 port = sock.getsockname()[1]
205 port = sock.getsockname()[1]
203 sock.close()
206 sock.close()
204 ports[i] = port
207 ports[i] = port
205 if xrep_port <= 0:
208 if xrep_port <= 0:
206 xrep_port = ports.pop(0)
209 xrep_port = ports.pop(0)
207 if pub_port <= 0:
210 if pub_port <= 0:
208 pub_port = ports.pop(0)
211 pub_port = ports.pop(0)
209 if req_port <= 0:
212 if req_port <= 0:
210 req_port = ports.pop(0)
213 req_port = ports.pop(0)
211 if hb_port <= 0:
214 if hb_port <= 0:
212 hb_port = ports.pop(0)
215 hb_port = ports.pop(0)
213
216
214 # Build the kernel launch command.
217 # Build the kernel launch command.
215 arguments = [ sys.executable, '-c', code, '--xrep', str(xrep_port),
218 if executable is None:
219 executable = sys.executable
220 arguments = [ executable, '-c', code, '--xrep', str(xrep_port),
216 '--pub', str(pub_port), '--req', str(req_port),
221 '--pub', str(pub_port), '--req', str(req_port),
217 '--hb', str(hb_port) ]
222 '--hb', str(hb_port) ]
218 arguments.extend(extra_arguments)
223 arguments.extend(extra_arguments)
219
224
220 # Spawn a kernel.
225 # Spawn a kernel.
221 if sys.platform == 'win32':
226 if sys.platform == 'win32':
222 # Create a Win32 event for interrupting the kernel.
227 # Create a Win32 event for interrupting the kernel.
223 interrupt_event = ParentPollerWindows.create_interrupt_event()
228 interrupt_event = ParentPollerWindows.create_interrupt_event()
224 arguments += [ '--interrupt', str(int(interrupt_event)) ]
229 arguments += [ '--interrupt', str(int(interrupt_event)) ]
225
230
226 # If using pythonw, stdin, stdout, and stderr are invalid. Popen will
231 # If using pythonw, stdin, stdout, and stderr are invalid. Popen will
227 # fail unless they are suitably redirected. We don't read from the
232 # fail unless they are suitably redirected. We don't read from the
228 # pipes, but they must exist.
233 # pipes, but they must exist.
229 redirect = PIPE if sys.executable.endswith('pythonw.exe') else None
234 redirect = PIPE if sys.executable.endswith('pythonw.exe') else None
230
235
231 if independent:
236 if independent:
232 proc = Popen(arguments,
237 proc = Popen(arguments,
233 creationflags=512, # CREATE_NEW_PROCESS_GROUP
238 creationflags=512, # CREATE_NEW_PROCESS_GROUP
234 stdout=redirect, stderr=redirect, stdin=redirect)
239 stdout=redirect, stderr=redirect, stdin=redirect)
235 else:
240 else:
236 from _subprocess import DuplicateHandle, GetCurrentProcess, \
241 from _subprocess import DuplicateHandle, GetCurrentProcess, \
237 DUPLICATE_SAME_ACCESS
242 DUPLICATE_SAME_ACCESS
238 pid = GetCurrentProcess()
243 pid = GetCurrentProcess()
239 handle = DuplicateHandle(pid, pid, pid, 0,
244 handle = DuplicateHandle(pid, pid, pid, 0,
240 True, # Inheritable by new processes.
245 True, # Inheritable by new processes.
241 DUPLICATE_SAME_ACCESS)
246 DUPLICATE_SAME_ACCESS)
242 proc = Popen(arguments + ['--parent', str(int(handle))],
247 proc = Popen(arguments + ['--parent', str(int(handle))],
243 stdout=redirect, stderr=redirect, stdin=redirect)
248 stdout=redirect, stderr=redirect, stdin=redirect)
244
249
245 # Attach the interrupt event to the Popen objet so it can be used later.
250 # Attach the interrupt event to the Popen objet so it can be used later.
246 proc.win32_interrupt_event = interrupt_event
251 proc.win32_interrupt_event = interrupt_event
247
252
248 # Clean up pipes created to work around Popen bug.
253 # Clean up pipes created to work around Popen bug.
249 if redirect is not None:
254 if redirect is not None:
250 proc.stdout.close()
255 proc.stdout.close()
251 proc.stderr.close()
256 proc.stderr.close()
252 proc.stdin.close()
257 proc.stdin.close()
253
258
254 else:
259 else:
255 if independent:
260 if independent:
256 proc = Popen(arguments, preexec_fn=lambda: os.setsid())
261 proc = Popen(arguments, preexec_fn=lambda: os.setsid())
257 else:
262 else:
258 proc = Popen(arguments + ['--parent'])
263 proc = Popen(arguments + ['--parent'])
259
264
260 return proc, xrep_port, pub_port, req_port, hb_port
265 return proc, xrep_port, pub_port, req_port, hb_port
@@ -1,659 +1,662
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 from __future__ import print_function
16 from __future__ import print_function
17
17
18 # Standard library imports.
18 # Standard library imports.
19 import __builtin__
19 import __builtin__
20 import atexit
20 import atexit
21 import sys
21 import sys
22 import time
22 import time
23 import traceback
23 import traceback
24 import logging
24 import logging
25 # System library imports.
25 # System library imports.
26 import zmq
26 import zmq
27
27
28 # Local imports.
28 # Local imports.
29 from IPython.config.configurable import Configurable
29 from IPython.config.configurable import Configurable
30 from IPython.utils import io
30 from IPython.utils import io
31 from IPython.utils.jsonutil import json_clean
31 from IPython.utils.jsonutil import json_clean
32 from IPython.lib import pylabtools
32 from IPython.lib import pylabtools
33 from IPython.utils.traitlets import Instance, Float
33 from IPython.utils.traitlets import Instance, Float
34 from entry_point import (base_launch_kernel, make_argument_parser, make_kernel,
34 from entry_point import (base_launch_kernel, make_argument_parser, make_kernel,
35 start_kernel)
35 start_kernel)
36 from iostream import OutStream
36 from iostream import OutStream
37 from session import Session, Message
37 from session import Session, Message
38 from zmqshell import ZMQInteractiveShell
38 from zmqshell import ZMQInteractiveShell
39
39
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41 # Globals
41 # Globals
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43
43
44 # Module-level logger
44 # Module-level logger
45 logger = logging.getLogger(__name__)
45 logger = logging.getLogger(__name__)
46
46
47 # FIXME: this needs to be done more cleanly later, once we have proper
47 # FIXME: this needs to be done more cleanly later, once we have proper
48 # configuration support. This is a library, so it shouldn't set a stream
48 # configuration support. This is a library, so it shouldn't set a stream
49 # handler, see:
49 # handler, see:
50 # http://docs.python.org/library/logging.html#configuring-logging-for-a-library
50 # http://docs.python.org/library/logging.html#configuring-logging-for-a-library
51 # But this lets us at least do developer debugging for now by manually turning
51 # But this lets us at least do developer debugging for now by manually turning
52 # it on/off. And once we have full config support, the client entry points
52 # it on/off. And once we have full config support, the client entry points
53 # will select their logging handlers, as well as passing to this library the
53 # will select their logging handlers, as well as passing to this library the
54 # logging level.
54 # logging level.
55
55
56 if 0: # dbg - set to 1 to actually see the messages.
56 if 0: # dbg - set to 1 to actually see the messages.
57 logger.addHandler(logging.StreamHandler())
57 logger.addHandler(logging.StreamHandler())
58 logger.setLevel(logging.DEBUG)
58 logger.setLevel(logging.DEBUG)
59
59
60 # /FIXME
60 # /FIXME
61
61
62 #-----------------------------------------------------------------------------
62 #-----------------------------------------------------------------------------
63 # Main kernel class
63 # Main kernel class
64 #-----------------------------------------------------------------------------
64 #-----------------------------------------------------------------------------
65
65
66 class Kernel(Configurable):
66 class Kernel(Configurable):
67
67
68 #---------------------------------------------------------------------------
68 #---------------------------------------------------------------------------
69 # Kernel interface
69 # Kernel interface
70 #---------------------------------------------------------------------------
70 #---------------------------------------------------------------------------
71
71
72 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
72 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
73 session = Instance(Session)
73 session = Instance(Session)
74 reply_socket = Instance('zmq.Socket')
74 reply_socket = Instance('zmq.Socket')
75 pub_socket = Instance('zmq.Socket')
75 pub_socket = Instance('zmq.Socket')
76 req_socket = Instance('zmq.Socket')
76 req_socket = Instance('zmq.Socket')
77
77
78 # Private interface
78 # Private interface
79
79
80 # Time to sleep after flushing the stdout/err buffers in each execute
80 # Time to sleep after flushing the stdout/err buffers in each execute
81 # cycle. While this introduces a hard limit on the minimal latency of the
81 # cycle. While this introduces a hard limit on the minimal latency of the
82 # execute cycle, it helps prevent output synchronization problems for
82 # execute cycle, it helps prevent output synchronization problems for
83 # clients.
83 # clients.
84 # Units are in seconds. The minimum zmq latency on local host is probably
84 # Units are in seconds. The minimum zmq latency on local host is probably
85 # ~150 microseconds, set this to 500us for now. We may need to increase it
85 # ~150 microseconds, set this to 500us for now. We may need to increase it
86 # a little if it's not enough after more interactive testing.
86 # a little if it's not enough after more interactive testing.
87 _execute_sleep = Float(0.0005, config=True)
87 _execute_sleep = Float(0.0005, config=True)
88
88
89 # Frequency of the kernel's event loop.
89 # Frequency of the kernel's event loop.
90 # Units are in seconds, kernel subclasses for GUI toolkits may need to
90 # Units are in seconds, kernel subclasses for GUI toolkits may need to
91 # adapt to milliseconds.
91 # adapt to milliseconds.
92 _poll_interval = Float(0.05, config=True)
92 _poll_interval = Float(0.05, config=True)
93
93
94 # If the shutdown was requested over the network, we leave here the
94 # If the shutdown was requested over the network, we leave here the
95 # necessary reply message so it can be sent by our registered atexit
95 # necessary reply message so it can be sent by our registered atexit
96 # handler. This ensures that the reply is only sent to clients truly at
96 # handler. This ensures that the reply is only sent to clients truly at
97 # the end of our shutdown process (which happens after the underlying
97 # the end of our shutdown process (which happens after the underlying
98 # IPython shell's own shutdown).
98 # IPython shell's own shutdown).
99 _shutdown_message = None
99 _shutdown_message = None
100
100
101 # This is a dict of port number that the kernel is listening on. It is set
101 # This is a dict of port number that the kernel is listening on. It is set
102 # by record_ports and used by connect_request.
102 # by record_ports and used by connect_request.
103 _recorded_ports = None
103 _recorded_ports = None
104
104
105
105
106 def __init__(self, **kwargs):
106 def __init__(self, **kwargs):
107 super(Kernel, self).__init__(**kwargs)
107 super(Kernel, self).__init__(**kwargs)
108
108
109 # Before we even start up the shell, register *first* our exit handlers
109 # Before we even start up the shell, register *first* our exit handlers
110 # so they come before the shell's
110 # so they come before the shell's
111 atexit.register(self._at_shutdown)
111 atexit.register(self._at_shutdown)
112
112
113 # Initialize the InteractiveShell subclass
113 # Initialize the InteractiveShell subclass
114 self.shell = ZMQInteractiveShell.instance()
114 self.shell = ZMQInteractiveShell.instance()
115 self.shell.displayhook.session = self.session
115 self.shell.displayhook.session = self.session
116 self.shell.displayhook.pub_socket = self.pub_socket
116 self.shell.displayhook.pub_socket = self.pub_socket
117 self.shell.display_pub.session = self.session
117 self.shell.display_pub.session = self.session
118 self.shell.display_pub.pub_socket = self.pub_socket
118 self.shell.display_pub.pub_socket = self.pub_socket
119
119
120 # TMP - hack while developing
120 # TMP - hack while developing
121 self.shell._reply_content = None
121 self.shell._reply_content = None
122
122
123 # Build dict of handlers for message types
123 # Build dict of handlers for message types
124 msg_types = [ 'execute_request', 'complete_request',
124 msg_types = [ 'execute_request', 'complete_request',
125 'object_info_request', 'history_tail_request',
125 'object_info_request', 'history_tail_request',
126 'connect_request', 'shutdown_request']
126 'connect_request', 'shutdown_request']
127 self.handlers = {}
127 self.handlers = {}
128 for msg_type in msg_types:
128 for msg_type in msg_types:
129 self.handlers[msg_type] = getattr(self, msg_type)
129 self.handlers[msg_type] = getattr(self, msg_type)
130
130
131 def do_one_iteration(self):
131 def do_one_iteration(self):
132 """Do one iteration of the kernel's evaluation loop.
132 """Do one iteration of the kernel's evaluation loop.
133 """
133 """
134 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
134 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
135 if msg is None:
135 if msg is None:
136 return
136 return
137
137
138 # This assert will raise in versions of zeromq 2.0.7 and lesser.
138 # This assert will raise in versions of zeromq 2.0.7 and lesser.
139 # We now require 2.0.8 or above, so we can uncomment for safety.
139 # We now require 2.0.8 or above, so we can uncomment for safety.
140 # print(ident,msg, file=sys.__stdout__)
140 # print(ident,msg, file=sys.__stdout__)
141 assert ident is not None, "Missing message part."
141 assert ident is not None, "Missing message part."
142
142
143 # Print some info about this message and leave a '--->' marker, so it's
143 # Print some info about this message and leave a '--->' marker, so it's
144 # easier to trace visually the message chain when debugging. Each
144 # easier to trace visually the message chain when debugging. Each
145 # handler prints its message at the end.
145 # handler prints its message at the end.
146 # Eventually we'll move these from stdout to a logger.
146 # Eventually we'll move these from stdout to a logger.
147 logger.debug('\n*** MESSAGE TYPE:'+str(msg['msg_type'])+'***')
147 logger.debug('\n*** MESSAGE TYPE:'+str(msg['msg_type'])+'***')
148 logger.debug(' Content: '+str(msg['content'])+'\n --->\n ')
148 logger.debug(' Content: '+str(msg['content'])+'\n --->\n ')
149
149
150 # Find and call actual handler for message
150 # Find and call actual handler for message
151 handler = self.handlers.get(msg['msg_type'], None)
151 handler = self.handlers.get(msg['msg_type'], None)
152 if handler is None:
152 if handler is None:
153 logger.error("UNKNOWN MESSAGE TYPE:" +str(msg))
153 logger.error("UNKNOWN MESSAGE TYPE:" +str(msg))
154 else:
154 else:
155 handler(ident, msg)
155 handler(ident, msg)
156
156
157 # Check whether we should exit, in case the incoming message set the
157 # Check whether we should exit, in case the incoming message set the
158 # exit flag on
158 # exit flag on
159 if self.shell.exit_now:
159 if self.shell.exit_now:
160 logger.debug('\nExiting IPython kernel...')
160 logger.debug('\nExiting IPython kernel...')
161 # We do a normal, clean exit, which allows any actions registered
161 # We do a normal, clean exit, which allows any actions registered
162 # via atexit (such as history saving) to take place.
162 # via atexit (such as history saving) to take place.
163 sys.exit(0)
163 sys.exit(0)
164
164
165
165
166 def start(self):
166 def start(self):
167 """ Start the kernel main loop.
167 """ Start the kernel main loop.
168 """
168 """
169 while True:
169 while True:
170 time.sleep(self._poll_interval)
170 time.sleep(self._poll_interval)
171 self.do_one_iteration()
171 self.do_one_iteration()
172
172
173 def record_ports(self, xrep_port, pub_port, req_port, hb_port):
173 def record_ports(self, xrep_port, pub_port, req_port, hb_port):
174 """Record the ports that this kernel is using.
174 """Record the ports that this kernel is using.
175
175
176 The creator of the Kernel instance must call this methods if they
176 The creator of the Kernel instance must call this methods if they
177 want the :meth:`connect_request` method to return the port numbers.
177 want the :meth:`connect_request` method to return the port numbers.
178 """
178 """
179 self._recorded_ports = {
179 self._recorded_ports = {
180 'xrep_port' : xrep_port,
180 'xrep_port' : xrep_port,
181 'pub_port' : pub_port,
181 'pub_port' : pub_port,
182 'req_port' : req_port,
182 'req_port' : req_port,
183 'hb_port' : hb_port
183 'hb_port' : hb_port
184 }
184 }
185
185
186 #---------------------------------------------------------------------------
186 #---------------------------------------------------------------------------
187 # Kernel request handlers
187 # Kernel request handlers
188 #---------------------------------------------------------------------------
188 #---------------------------------------------------------------------------
189
189
190 def _publish_pyin(self, code, parent):
190 def _publish_pyin(self, code, parent):
191 """Publish the code request on the pyin stream."""
191 """Publish the code request on the pyin stream."""
192
192
193 pyin_msg = self.session.send(self.pub_socket, u'pyin',{u'code':code}, parent=parent)
193 pyin_msg = self.session.send(self.pub_socket, u'pyin',{u'code':code}, parent=parent)
194
194
195 def execute_request(self, ident, parent):
195 def execute_request(self, ident, parent):
196
196
197 status_msg = self.session.send(self.pub_socket,
197 status_msg = self.session.send(self.pub_socket,
198 u'status',
198 u'status',
199 {u'execution_state':u'busy'},
199 {u'execution_state':u'busy'},
200 parent=parent
200 parent=parent
201 )
201 )
202
202
203 try:
203 try:
204 content = parent[u'content']
204 content = parent[u'content']
205 code = content[u'code']
205 code = content[u'code']
206 silent = content[u'silent']
206 silent = content[u'silent']
207 except:
207 except:
208 logger.error("Got bad msg: ")
208 logger.error("Got bad msg: ")
209 logger.error(str(Message(parent)))
209 logger.error(str(Message(parent)))
210 return
210 return
211
211
212 shell = self.shell # we'll need this a lot here
212 shell = self.shell # we'll need this a lot here
213
213
214 # Replace raw_input. Note that is not sufficient to replace
214 # Replace raw_input. Note that is not sufficient to replace
215 # raw_input in the user namespace.
215 # raw_input in the user namespace.
216 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
216 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
217 __builtin__.raw_input = raw_input
217 __builtin__.raw_input = raw_input
218
218
219 # Set the parent message of the display hook and out streams.
219 # Set the parent message of the display hook and out streams.
220 shell.displayhook.set_parent(parent)
220 shell.displayhook.set_parent(parent)
221 shell.display_pub.set_parent(parent)
221 shell.display_pub.set_parent(parent)
222 sys.stdout.set_parent(parent)
222 sys.stdout.set_parent(parent)
223 sys.stderr.set_parent(parent)
223 sys.stderr.set_parent(parent)
224
224
225 # Re-broadcast our input for the benefit of listening clients, and
225 # Re-broadcast our input for the benefit of listening clients, and
226 # start computing output
226 # start computing output
227 if not silent:
227 if not silent:
228 self._publish_pyin(code, parent)
228 self._publish_pyin(code, parent)
229
229
230 reply_content = {}
230 reply_content = {}
231 try:
231 try:
232 if silent:
232 if silent:
233 # run_code uses 'exec' mode, so no displayhook will fire, and it
233 # run_code uses 'exec' mode, so no displayhook will fire, and it
234 # doesn't call logging or history manipulations. Print
234 # doesn't call logging or history manipulations. Print
235 # statements in that code will obviously still execute.
235 # statements in that code will obviously still execute.
236 shell.run_code(code)
236 shell.run_code(code)
237 else:
237 else:
238 # FIXME: the shell calls the exception handler itself.
238 # FIXME: the shell calls the exception handler itself.
239 shell._reply_content = None
239 shell._reply_content = None
240 shell.run_cell(code)
240 shell.run_cell(code)
241 except:
241 except:
242 status = u'error'
242 status = u'error'
243 # FIXME: this code right now isn't being used yet by default,
243 # FIXME: this code right now isn't being used yet by default,
244 # because the run_cell() call above directly fires off exception
244 # because the run_cell() call above directly fires off exception
245 # reporting. This code, therefore, is only active in the scenario
245 # reporting. This code, therefore, is only active in the scenario
246 # where runlines itself has an unhandled exception. We need to
246 # where runlines itself has an unhandled exception. We need to
247 # uniformize this, for all exception construction to come from a
247 # uniformize this, for all exception construction to come from a
248 # single location in the codbase.
248 # single location in the codbase.
249 etype, evalue, tb = sys.exc_info()
249 etype, evalue, tb = sys.exc_info()
250 tb_list = traceback.format_exception(etype, evalue, tb)
250 tb_list = traceback.format_exception(etype, evalue, tb)
251 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
251 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
252 else:
252 else:
253 status = u'ok'
253 status = u'ok'
254
254
255 reply_content[u'status'] = status
255 reply_content[u'status'] = status
256
256
257 # Return the execution counter so clients can display prompts
257 # Return the execution counter so clients can display prompts
258 reply_content['execution_count'] = shell.execution_count -1
258 reply_content['execution_count'] = shell.execution_count -1
259
259
260 # FIXME - fish exception info out of shell, possibly left there by
260 # FIXME - fish exception info out of shell, possibly left there by
261 # runlines. We'll need to clean up this logic later.
261 # runlines. We'll need to clean up this logic later.
262 if shell._reply_content is not None:
262 if shell._reply_content is not None:
263 reply_content.update(shell._reply_content)
263 reply_content.update(shell._reply_content)
264
264
265 # At this point, we can tell whether the main code execution succeeded
265 # At this point, we can tell whether the main code execution succeeded
266 # or not. If it did, we proceed to evaluate user_variables/expressions
266 # or not. If it did, we proceed to evaluate user_variables/expressions
267 if reply_content['status'] == 'ok':
267 if reply_content['status'] == 'ok':
268 reply_content[u'user_variables'] = \
268 reply_content[u'user_variables'] = \
269 shell.user_variables(content[u'user_variables'])
269 shell.user_variables(content[u'user_variables'])
270 reply_content[u'user_expressions'] = \
270 reply_content[u'user_expressions'] = \
271 shell.user_expressions(content[u'user_expressions'])
271 shell.user_expressions(content[u'user_expressions'])
272 else:
272 else:
273 # If there was an error, don't even try to compute variables or
273 # If there was an error, don't even try to compute variables or
274 # expressions
274 # expressions
275 reply_content[u'user_variables'] = {}
275 reply_content[u'user_variables'] = {}
276 reply_content[u'user_expressions'] = {}
276 reply_content[u'user_expressions'] = {}
277
277
278 # Payloads should be retrieved regardless of outcome, so we can both
278 # Payloads should be retrieved regardless of outcome, so we can both
279 # recover partial output (that could have been generated early in a
279 # recover partial output (that could have been generated early in a
280 # block, before an error) and clear the payload system always.
280 # block, before an error) and clear the payload system always.
281 reply_content[u'payload'] = shell.payload_manager.read_payload()
281 reply_content[u'payload'] = shell.payload_manager.read_payload()
282 # Be agressive about clearing the payload because we don't want
282 # Be agressive about clearing the payload because we don't want
283 # it to sit in memory until the next execute_request comes in.
283 # it to sit in memory until the next execute_request comes in.
284 shell.payload_manager.clear_payload()
284 shell.payload_manager.clear_payload()
285
285
286 # Flush output before sending the reply.
286 # Flush output before sending the reply.
287 sys.stdout.flush()
287 sys.stdout.flush()
288 sys.stderr.flush()
288 sys.stderr.flush()
289 # FIXME: on rare occasions, the flush doesn't seem to make it to the
289 # FIXME: on rare occasions, the flush doesn't seem to make it to the
290 # clients... This seems to mitigate the problem, but we definitely need
290 # clients... This seems to mitigate the problem, but we definitely need
291 # to better understand what's going on.
291 # to better understand what's going on.
292 if self._execute_sleep:
292 if self._execute_sleep:
293 time.sleep(self._execute_sleep)
293 time.sleep(self._execute_sleep)
294
294
295 # Send the reply.
295 # Send the reply.
296 reply_msg = self.session.send(self.reply_socket, u'execute_reply',
296 reply_msg = self.session.send(self.reply_socket, u'execute_reply',
297 reply_content, parent, ident=ident)
297 reply_content, parent, ident=ident)
298 logger.debug(str(reply_msg))
298 logger.debug(str(reply_msg))
299
299
300 if reply_msg['content']['status'] == u'error':
300 if reply_msg['content']['status'] == u'error':
301 self._abort_queue()
301 self._abort_queue()
302
302
303 status_msg = self.session.send(self.pub_socket,
303 status_msg = self.session.send(self.pub_socket,
304 u'status',
304 u'status',
305 {u'execution_state':u'idle'},
305 {u'execution_state':u'idle'},
306 parent=parent
306 parent=parent
307 )
307 )
308
308
309 def complete_request(self, ident, parent):
309 def complete_request(self, ident, parent):
310 txt, matches = self._complete(parent)
310 txt, matches = self._complete(parent)
311 matches = {'matches' : matches,
311 matches = {'matches' : matches,
312 'matched_text' : txt,
312 'matched_text' : txt,
313 'status' : 'ok'}
313 'status' : 'ok'}
314 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
314 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
315 matches, parent, ident)
315 matches, parent, ident)
316 logger.debug(str(completion_msg))
316 logger.debug(str(completion_msg))
317
317
318 def object_info_request(self, ident, parent):
318 def object_info_request(self, ident, parent):
319 object_info = self.shell.object_inspect(parent['content']['oname'])
319 object_info = self.shell.object_inspect(parent['content']['oname'])
320 # Before we send this object over, we scrub it for JSON usage
320 # Before we send this object over, we scrub it for JSON usage
321 oinfo = json_clean(object_info)
321 oinfo = json_clean(object_info)
322 msg = self.session.send(self.reply_socket, 'object_info_reply',
322 msg = self.session.send(self.reply_socket, 'object_info_reply',
323 oinfo, parent, ident)
323 oinfo, parent, ident)
324 logger.debug(msg)
324 logger.debug(msg)
325
325
326 def history_tail_request(self, ident, parent):
326 def history_tail_request(self, ident, parent):
327 # We need to pull these out, as passing **kwargs doesn't work with
327 # We need to pull these out, as passing **kwargs doesn't work with
328 # unicode keys before Python 2.6.5.
328 # unicode keys before Python 2.6.5.
329 n = parent['content']['n']
329 n = parent['content']['n']
330 raw = parent['content']['raw']
330 raw = parent['content']['raw']
331 output = parent['content']['output']
331 output = parent['content']['output']
332 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output)
332 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output)
333 content = {'history' : list(hist)}
333 content = {'history' : list(hist)}
334 msg = self.session.send(self.reply_socket, 'history_tail_reply',
334 msg = self.session.send(self.reply_socket, 'history_tail_reply',
335 content, parent, ident)
335 content, parent, ident)
336 logger.debug(str(msg))
336 logger.debug(str(msg))
337
337
338 def connect_request(self, ident, parent):
338 def connect_request(self, ident, parent):
339 if self._recorded_ports is not None:
339 if self._recorded_ports is not None:
340 content = self._recorded_ports.copy()
340 content = self._recorded_ports.copy()
341 else:
341 else:
342 content = {}
342 content = {}
343 msg = self.session.send(self.reply_socket, 'connect_reply',
343 msg = self.session.send(self.reply_socket, 'connect_reply',
344 content, parent, ident)
344 content, parent, ident)
345 logger.debug(msg)
345 logger.debug(msg)
346
346
347 def shutdown_request(self, ident, parent):
347 def shutdown_request(self, ident, parent):
348 self.shell.exit_now = True
348 self.shell.exit_now = True
349 self._shutdown_message = self.session.msg(u'shutdown_reply', parent['content'], parent)
349 self._shutdown_message = self.session.msg(u'shutdown_reply', parent['content'], parent)
350 sys.exit(0)
350 sys.exit(0)
351
351
352 #---------------------------------------------------------------------------
352 #---------------------------------------------------------------------------
353 # Protected interface
353 # Protected interface
354 #---------------------------------------------------------------------------
354 #---------------------------------------------------------------------------
355
355
356 def _abort_queue(self):
356 def _abort_queue(self):
357 while True:
357 while True:
358 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
358 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
359 if msg is None:
359 if msg is None:
360 break
360 break
361 else:
361 else:
362 assert ident is not None, \
362 assert ident is not None, \
363 "Unexpected missing message part."
363 "Unexpected missing message part."
364
364
365 logger.debug("Aborting:\n"+str(Message(msg)))
365 logger.debug("Aborting:\n"+str(Message(msg)))
366 msg_type = msg['msg_type']
366 msg_type = msg['msg_type']
367 reply_type = msg_type.split('_')[0] + '_reply'
367 reply_type = msg_type.split('_')[0] + '_reply'
368 reply_msg = self.session.send(self.reply_socket, reply_type,
368 reply_msg = self.session.send(self.reply_socket, reply_type,
369 {'status' : 'aborted'}, msg, ident=ident)
369 {'status' : 'aborted'}, msg, ident=ident)
370 logger.debug(reply_msg)
370 logger.debug(reply_msg)
371 # We need to wait a bit for requests to come in. This can probably
371 # We need to wait a bit for requests to come in. This can probably
372 # be set shorter for true asynchronous clients.
372 # be set shorter for true asynchronous clients.
373 time.sleep(0.1)
373 time.sleep(0.1)
374
374
375 def _raw_input(self, prompt, ident, parent):
375 def _raw_input(self, prompt, ident, parent):
376 # Flush output before making the request.
376 # Flush output before making the request.
377 sys.stderr.flush()
377 sys.stderr.flush()
378 sys.stdout.flush()
378 sys.stdout.flush()
379
379
380 # Send the input request.
380 # Send the input request.
381 content = dict(prompt=prompt)
381 content = dict(prompt=prompt)
382 msg = self.session.send(self.req_socket, u'input_request', content, parent)
382 msg = self.session.send(self.req_socket, u'input_request', content, parent)
383
383
384 # Await a response.
384 # Await a response.
385 ident, reply = self.session.recv(self.req_socket, 0)
385 ident, reply = self.session.recv(self.req_socket, 0)
386 try:
386 try:
387 value = reply['content']['value']
387 value = reply['content']['value']
388 except:
388 except:
389 logger.error("Got bad raw_input reply: ")
389 logger.error("Got bad raw_input reply: ")
390 logger.error(str(Message(parent)))
390 logger.error(str(Message(parent)))
391 value = ''
391 value = ''
392 return value
392 return value
393
393
394 def _complete(self, msg):
394 def _complete(self, msg):
395 c = msg['content']
395 c = msg['content']
396 try:
396 try:
397 cpos = int(c['cursor_pos'])
397 cpos = int(c['cursor_pos'])
398 except:
398 except:
399 # If we don't get something that we can convert to an integer, at
399 # If we don't get something that we can convert to an integer, at
400 # least attempt the completion guessing the cursor is at the end of
400 # least attempt the completion guessing the cursor is at the end of
401 # the text, if there's any, and otherwise of the line
401 # the text, if there's any, and otherwise of the line
402 cpos = len(c['text'])
402 cpos = len(c['text'])
403 if cpos==0:
403 if cpos==0:
404 cpos = len(c['line'])
404 cpos = len(c['line'])
405 return self.shell.complete(c['text'], c['line'], cpos)
405 return self.shell.complete(c['text'], c['line'], cpos)
406
406
407 def _object_info(self, context):
407 def _object_info(self, context):
408 symbol, leftover = self._symbol_from_context(context)
408 symbol, leftover = self._symbol_from_context(context)
409 if symbol is not None and not leftover:
409 if symbol is not None and not leftover:
410 doc = getattr(symbol, '__doc__', '')
410 doc = getattr(symbol, '__doc__', '')
411 else:
411 else:
412 doc = ''
412 doc = ''
413 object_info = dict(docstring = doc)
413 object_info = dict(docstring = doc)
414 return object_info
414 return object_info
415
415
416 def _symbol_from_context(self, context):
416 def _symbol_from_context(self, context):
417 if not context:
417 if not context:
418 return None, context
418 return None, context
419
419
420 base_symbol_string = context[0]
420 base_symbol_string = context[0]
421 symbol = self.shell.user_ns.get(base_symbol_string, None)
421 symbol = self.shell.user_ns.get(base_symbol_string, None)
422 if symbol is None:
422 if symbol is None:
423 symbol = __builtin__.__dict__.get(base_symbol_string, None)
423 symbol = __builtin__.__dict__.get(base_symbol_string, None)
424 if symbol is None:
424 if symbol is None:
425 return None, context
425 return None, context
426
426
427 context = context[1:]
427 context = context[1:]
428 for i, name in enumerate(context):
428 for i, name in enumerate(context):
429 new_symbol = getattr(symbol, name, None)
429 new_symbol = getattr(symbol, name, None)
430 if new_symbol is None:
430 if new_symbol is None:
431 return symbol, context[i:]
431 return symbol, context[i:]
432 else:
432 else:
433 symbol = new_symbol
433 symbol = new_symbol
434
434
435 return symbol, []
435 return symbol, []
436
436
437 def _at_shutdown(self):
437 def _at_shutdown(self):
438 """Actions taken at shutdown by the kernel, called by python's atexit.
438 """Actions taken at shutdown by the kernel, called by python's atexit.
439 """
439 """
440 # io.rprint("Kernel at_shutdown") # dbg
440 # io.rprint("Kernel at_shutdown") # dbg
441 if self._shutdown_message is not None:
441 if self._shutdown_message is not None:
442 self.session.send(self.reply_socket, self._shutdown_message)
442 self.session.send(self.reply_socket, self._shutdown_message)
443 self.session.send(self.pub_socket, self._shutdown_message)
443 self.session.send(self.pub_socket, self._shutdown_message)
444 logger.debug(str(self._shutdown_message))
444 logger.debug(str(self._shutdown_message))
445 # A very short sleep to give zmq time to flush its message buffers
445 # A very short sleep to give zmq time to flush its message buffers
446 # before Python truly shuts down.
446 # before Python truly shuts down.
447 time.sleep(0.01)
447 time.sleep(0.01)
448
448
449
449
450 class QtKernel(Kernel):
450 class QtKernel(Kernel):
451 """A Kernel subclass with Qt support."""
451 """A Kernel subclass with Qt support."""
452
452
453 def start(self):
453 def start(self):
454 """Start a kernel with QtPy4 event loop integration."""
454 """Start a kernel with QtPy4 event loop integration."""
455
455
456 from PyQt4 import QtCore
456 from PyQt4 import QtCore
457 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
457 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
458
458
459 self.app = get_app_qt4([" "])
459 self.app = get_app_qt4([" "])
460 self.app.setQuitOnLastWindowClosed(False)
460 self.app.setQuitOnLastWindowClosed(False)
461 self.timer = QtCore.QTimer()
461 self.timer = QtCore.QTimer()
462 self.timer.timeout.connect(self.do_one_iteration)
462 self.timer.timeout.connect(self.do_one_iteration)
463 # Units for the timer are in milliseconds
463 # Units for the timer are in milliseconds
464 self.timer.start(1000*self._poll_interval)
464 self.timer.start(1000*self._poll_interval)
465 start_event_loop_qt4(self.app)
465 start_event_loop_qt4(self.app)
466
466
467
467
468 class WxKernel(Kernel):
468 class WxKernel(Kernel):
469 """A Kernel subclass with Wx support."""
469 """A Kernel subclass with Wx support."""
470
470
471 def start(self):
471 def start(self):
472 """Start a kernel with wx event loop support."""
472 """Start a kernel with wx event loop support."""
473
473
474 import wx
474 import wx
475 from IPython.lib.guisupport import start_event_loop_wx
475 from IPython.lib.guisupport import start_event_loop_wx
476
476
477 doi = self.do_one_iteration
477 doi = self.do_one_iteration
478 # Wx uses milliseconds
478 # Wx uses milliseconds
479 poll_interval = int(1000*self._poll_interval)
479 poll_interval = int(1000*self._poll_interval)
480
480
481 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
481 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
482 # We make the Frame hidden when we create it in the main app below.
482 # We make the Frame hidden when we create it in the main app below.
483 class TimerFrame(wx.Frame):
483 class TimerFrame(wx.Frame):
484 def __init__(self, func):
484 def __init__(self, func):
485 wx.Frame.__init__(self, None, -1)
485 wx.Frame.__init__(self, None, -1)
486 self.timer = wx.Timer(self)
486 self.timer = wx.Timer(self)
487 # Units for the timer are in milliseconds
487 # Units for the timer are in milliseconds
488 self.timer.Start(poll_interval)
488 self.timer.Start(poll_interval)
489 self.Bind(wx.EVT_TIMER, self.on_timer)
489 self.Bind(wx.EVT_TIMER, self.on_timer)
490 self.func = func
490 self.func = func
491
491
492 def on_timer(self, event):
492 def on_timer(self, event):
493 self.func()
493 self.func()
494
494
495 # We need a custom wx.App to create our Frame subclass that has the
495 # We need a custom wx.App to create our Frame subclass that has the
496 # wx.Timer to drive the ZMQ event loop.
496 # wx.Timer to drive the ZMQ event loop.
497 class IPWxApp(wx.App):
497 class IPWxApp(wx.App):
498 def OnInit(self):
498 def OnInit(self):
499 self.frame = TimerFrame(doi)
499 self.frame = TimerFrame(doi)
500 self.frame.Show(False)
500 self.frame.Show(False)
501 return True
501 return True
502
502
503 # The redirect=False here makes sure that wx doesn't replace
503 # The redirect=False here makes sure that wx doesn't replace
504 # sys.stdout/stderr with its own classes.
504 # sys.stdout/stderr with its own classes.
505 self.app = IPWxApp(redirect=False)
505 self.app = IPWxApp(redirect=False)
506 start_event_loop_wx(self.app)
506 start_event_loop_wx(self.app)
507
507
508
508
509 class TkKernel(Kernel):
509 class TkKernel(Kernel):
510 """A Kernel subclass with Tk support."""
510 """A Kernel subclass with Tk support."""
511
511
512 def start(self):
512 def start(self):
513 """Start a Tk enabled event loop."""
513 """Start a Tk enabled event loop."""
514
514
515 import Tkinter
515 import Tkinter
516 doi = self.do_one_iteration
516 doi = self.do_one_iteration
517 # Tk uses milliseconds
517 # Tk uses milliseconds
518 poll_interval = int(1000*self._poll_interval)
518 poll_interval = int(1000*self._poll_interval)
519 # For Tkinter, we create a Tk object and call its withdraw method.
519 # For Tkinter, we create a Tk object and call its withdraw method.
520 class Timer(object):
520 class Timer(object):
521 def __init__(self, func):
521 def __init__(self, func):
522 self.app = Tkinter.Tk()
522 self.app = Tkinter.Tk()
523 self.app.withdraw()
523 self.app.withdraw()
524 self.func = func
524 self.func = func
525
525
526 def on_timer(self):
526 def on_timer(self):
527 self.func()
527 self.func()
528 self.app.after(poll_interval, self.on_timer)
528 self.app.after(poll_interval, self.on_timer)
529
529
530 def start(self):
530 def start(self):
531 self.on_timer() # Call it once to get things going.
531 self.on_timer() # Call it once to get things going.
532 self.app.mainloop()
532 self.app.mainloop()
533
533
534 self.timer = Timer(doi)
534 self.timer = Timer(doi)
535 self.timer.start()
535 self.timer.start()
536
536
537
537
538 class GTKKernel(Kernel):
538 class GTKKernel(Kernel):
539 """A Kernel subclass with GTK support."""
539 """A Kernel subclass with GTK support."""
540
540
541 def start(self):
541 def start(self):
542 """Start the kernel, coordinating with the GTK event loop"""
542 """Start the kernel, coordinating with the GTK event loop"""
543 from .gui.gtkembed import GTKEmbed
543 from .gui.gtkembed import GTKEmbed
544
544
545 gtk_kernel = GTKEmbed(self)
545 gtk_kernel = GTKEmbed(self)
546 gtk_kernel.start()
546 gtk_kernel.start()
547
547
548
548
549 #-----------------------------------------------------------------------------
549 #-----------------------------------------------------------------------------
550 # Kernel main and launch functions
550 # Kernel main and launch functions
551 #-----------------------------------------------------------------------------
551 #-----------------------------------------------------------------------------
552
552
553 def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
553 def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
554 independent=False, pylab=False, colors=None):
554 executable=None, independent=False, pylab=False, colors=None):
555 """Launches a localhost kernel, binding to the specified ports.
555 """Launches a localhost kernel, binding to the specified ports.
556
556
557 Parameters
557 Parameters
558 ----------
558 ----------
559 ip : str, optional
559 ip : str, optional
560 The ip address the kernel will bind to.
560 The ip address the kernel will bind to.
561
561
562 xrep_port : int, optional
562 xrep_port : int, optional
563 The port to use for XREP channel.
563 The port to use for XREP channel.
564
564
565 pub_port : int, optional
565 pub_port : int, optional
566 The port to use for the SUB channel.
566 The port to use for the SUB channel.
567
567
568 req_port : int, optional
568 req_port : int, optional
569 The port to use for the REQ (raw input) channel.
569 The port to use for the REQ (raw input) channel.
570
570
571 hb_port : int, optional
571 hb_port : int, optional
572 The port to use for the hearbeat REP channel.
572 The port to use for the hearbeat REP channel.
573
573
574 executable : str, optional (default sys.executable)
575 The Python executable to use for the kernel process.
576
574 independent : bool, optional (default False)
577 independent : bool, optional (default False)
575 If set, the kernel process is guaranteed to survive if this process
578 If set, the kernel process is guaranteed to survive if this process
576 dies. If not set, an effort is made to ensure that the kernel is killed
579 dies. If not set, an effort is made to ensure that the kernel is killed
577 when this process dies. Note that in this case it is still good practice
580 when this process dies. Note that in this case it is still good practice
578 to kill kernels manually before exiting.
581 to kill kernels manually before exiting.
579
582
580 pylab : bool or string, optional (default False)
583 pylab : bool or string, optional (default False)
581 If not False, the kernel will be launched with pylab enabled. If a
584 If not False, the kernel will be launched with pylab enabled. If a
582 string is passed, matplotlib will use the specified backend. Otherwise,
585 string is passed, matplotlib will use the specified backend. Otherwise,
583 matplotlib's default backend will be used.
586 matplotlib's default backend will be used.
584
587
585 colors : None or string, optional (default None)
588 colors : None or string, optional (default None)
586 If not None, specify the color scheme. One of (NoColor, LightBG, Linux)
589 If not None, specify the color scheme. One of (NoColor, LightBG, Linux)
587
590
588 Returns
591 Returns
589 -------
592 -------
590 A tuple of form:
593 A tuple of form:
591 (kernel_process, xrep_port, pub_port, req_port)
594 (kernel_process, xrep_port, pub_port, req_port)
592 where kernel_process is a Popen object and the ports are integers.
595 where kernel_process is a Popen object and the ports are integers.
593 """
596 """
594 extra_arguments = []
597 extra_arguments = []
595 if pylab:
598 if pylab:
596 extra_arguments.append('--pylab')
599 extra_arguments.append('--pylab')
597 if isinstance(pylab, basestring):
600 if isinstance(pylab, basestring):
598 extra_arguments.append(pylab)
601 extra_arguments.append(pylab)
599 if ip is not None:
602 if ip is not None:
600 extra_arguments.append('--ip')
603 extra_arguments.append('--ip')
601 if isinstance(ip, basestring):
604 if isinstance(ip, basestring):
602 extra_arguments.append(ip)
605 extra_arguments.append(ip)
603 if colors is not None:
606 if colors is not None:
604 extra_arguments.append('--colors')
607 extra_arguments.append('--colors')
605 extra_arguments.append(colors)
608 extra_arguments.append(colors)
606 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
609 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
607 xrep_port, pub_port, req_port, hb_port,
610 xrep_port, pub_port, req_port, hb_port,
608 independent, extra_arguments)
611 executable, independent, extra_arguments)
609
612
610
613
611 def main():
614 def main():
612 """ The IPython kernel main entry point.
615 """ The IPython kernel main entry point.
613 """
616 """
614 parser = make_argument_parser()
617 parser = make_argument_parser()
615 parser.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
618 parser.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
616 const='auto', help = \
619 const='auto', help = \
617 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
620 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
618 given, the GUI backend is matplotlib's, otherwise use one of: \
621 given, the GUI backend is matplotlib's, otherwise use one of: \
619 ['tk', 'gtk', 'qt', 'wx', 'osx', 'inline'].")
622 ['tk', 'gtk', 'qt', 'wx', 'osx', 'inline'].")
620 parser.add_argument('--colors',
623 parser.add_argument('--colors',
621 type=str, dest='colors',
624 type=str, dest='colors',
622 help="Set the color scheme (NoColor, Linux, and LightBG).",
625 help="Set the color scheme (NoColor, Linux, and LightBG).",
623 metavar='ZMQInteractiveShell.colors')
626 metavar='ZMQInteractiveShell.colors')
624 namespace = parser.parse_args()
627 namespace = parser.parse_args()
625
628
626 kernel_class = Kernel
629 kernel_class = Kernel
627
630
628 kernel_classes = {
631 kernel_classes = {
629 'qt' : QtKernel,
632 'qt' : QtKernel,
630 'qt4': QtKernel,
633 'qt4': QtKernel,
631 'inline': Kernel,
634 'inline': Kernel,
632 'osx': TkKernel,
635 'osx': TkKernel,
633 'wx' : WxKernel,
636 'wx' : WxKernel,
634 'tk' : TkKernel,
637 'tk' : TkKernel,
635 'gtk': GTKKernel,
638 'gtk': GTKKernel,
636 }
639 }
637 if namespace.pylab:
640 if namespace.pylab:
638 if namespace.pylab == 'auto':
641 if namespace.pylab == 'auto':
639 gui, backend = pylabtools.find_gui_and_backend()
642 gui, backend = pylabtools.find_gui_and_backend()
640 else:
643 else:
641 gui, backend = pylabtools.find_gui_and_backend(namespace.pylab)
644 gui, backend = pylabtools.find_gui_and_backend(namespace.pylab)
642 kernel_class = kernel_classes.get(gui)
645 kernel_class = kernel_classes.get(gui)
643 if kernel_class is None:
646 if kernel_class is None:
644 raise ValueError('GUI is not supported: %r' % gui)
647 raise ValueError('GUI is not supported: %r' % gui)
645 pylabtools.activate_matplotlib(backend)
648 pylabtools.activate_matplotlib(backend)
646 if namespace.colors:
649 if namespace.colors:
647 ZMQInteractiveShell.colors=namespace.colors
650 ZMQInteractiveShell.colors=namespace.colors
648
651
649 kernel = make_kernel(namespace, kernel_class, OutStream)
652 kernel = make_kernel(namespace, kernel_class, OutStream)
650
653
651 if namespace.pylab:
654 if namespace.pylab:
652 pylabtools.import_pylab(kernel.shell.user_ns, backend,
655 pylabtools.import_pylab(kernel.shell.user_ns, backend,
653 shell=kernel.shell)
656 shell=kernel.shell)
654
657
655 start_kernel(namespace, kernel)
658 start_kernel(namespace, kernel)
656
659
657
660
658 if __name__ == '__main__':
661 if __name__ == '__main__':
659 main()
662 main()
@@ -1,296 +1,300
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 sys
20 import sys
21 import time
21 import time
22 import traceback
22 import traceback
23
23
24 # System library imports.
24 # System library imports.
25 import zmq
25 import zmq
26
26
27 # Local imports.
27 # Local imports.
28 from IPython.utils.traitlets import HasTraits, Instance
28 from IPython.utils.traitlets import HasTraits, Instance
29 from completer import KernelCompleter
29 from completer import KernelCompleter
30 from entry_point import base_launch_kernel, make_default_main
30 from entry_point import base_launch_kernel, make_default_main
31 from session import Session, Message
31 from session import Session, Message
32
32
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34 # Main kernel class
34 # Main kernel class
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36
36
37 class Kernel(HasTraits):
37 class Kernel(HasTraits):
38
38
39 # Private interface
39 # Private interface
40
40
41 # This is a dict of port number that the kernel is listening on. It is set
41 # This is a dict of port number that the kernel is listening on. It is set
42 # by record_ports and used by connect_request.
42 # by record_ports and used by connect_request.
43 _recorded_ports = None
43 _recorded_ports = None
44
44
45 #---------------------------------------------------------------------------
45 #---------------------------------------------------------------------------
46 # Kernel interface
46 # Kernel interface
47 #---------------------------------------------------------------------------
47 #---------------------------------------------------------------------------
48
48
49 session = Instance(Session)
49 session = Instance(Session)
50 reply_socket = Instance('zmq.Socket')
50 reply_socket = Instance('zmq.Socket')
51 pub_socket = Instance('zmq.Socket')
51 pub_socket = Instance('zmq.Socket')
52 req_socket = Instance('zmq.Socket')
52 req_socket = Instance('zmq.Socket')
53
53
54 def __init__(self, **kwargs):
54 def __init__(self, **kwargs):
55 super(Kernel, self).__init__(**kwargs)
55 super(Kernel, self).__init__(**kwargs)
56 self.user_ns = {}
56 self.user_ns = {}
57 self.history = []
57 self.history = []
58 self.compiler = CommandCompiler()
58 self.compiler = CommandCompiler()
59 self.completer = KernelCompleter(self.user_ns)
59 self.completer = KernelCompleter(self.user_ns)
60
60
61 # Build dict of handlers for message types
61 # Build dict of handlers for message types
62 msg_types = [ 'execute_request', 'complete_request',
62 msg_types = [ 'execute_request', 'complete_request',
63 'object_info_request', 'shutdown_request' ]
63 'object_info_request', 'shutdown_request' ]
64 self.handlers = {}
64 self.handlers = {}
65 for msg_type in msg_types:
65 for msg_type in msg_types:
66 self.handlers[msg_type] = getattr(self, msg_type)
66 self.handlers[msg_type] = getattr(self, msg_type)
67
67
68 def start(self):
68 def start(self):
69 """ Start the kernel main loop.
69 """ Start the kernel main loop.
70 """
70 """
71 while True:
71 while True:
72 ident,msg = self.session.recv(self.reply_socket,0)
72 ident,msg = self.session.recv(self.reply_socket,0)
73 assert ident is not None, "Missing message part."
73 assert ident is not None, "Missing message part."
74 omsg = Message(msg)
74 omsg = Message(msg)
75 print>>sys.__stdout__
75 print>>sys.__stdout__
76 print>>sys.__stdout__, omsg
76 print>>sys.__stdout__, omsg
77 handler = self.handlers.get(omsg.msg_type, None)
77 handler = self.handlers.get(omsg.msg_type, None)
78 if handler is None:
78 if handler is None:
79 print >> sys.__stderr__, "UNKNOWN MESSAGE TYPE:", omsg
79 print >> sys.__stderr__, "UNKNOWN MESSAGE TYPE:", omsg
80 else:
80 else:
81 handler(ident, omsg)
81 handler(ident, omsg)
82
82
83 def record_ports(self, xrep_port, pub_port, req_port, hb_port):
83 def record_ports(self, xrep_port, pub_port, req_port, hb_port):
84 """Record the ports that this kernel is using.
84 """Record the ports that this kernel is using.
85
85
86 The creator of the Kernel instance must call this methods if they
86 The creator of the Kernel instance must call this methods if they
87 want the :meth:`connect_request` method to return the port numbers.
87 want the :meth:`connect_request` method to return the port numbers.
88 """
88 """
89 self._recorded_ports = {
89 self._recorded_ports = {
90 'xrep_port' : xrep_port,
90 'xrep_port' : xrep_port,
91 'pub_port' : pub_port,
91 'pub_port' : pub_port,
92 'req_port' : req_port,
92 'req_port' : req_port,
93 'hb_port' : hb_port
93 'hb_port' : hb_port
94 }
94 }
95
95
96 #---------------------------------------------------------------------------
96 #---------------------------------------------------------------------------
97 # Kernel request handlers
97 # Kernel request handlers
98 #---------------------------------------------------------------------------
98 #---------------------------------------------------------------------------
99
99
100 def execute_request(self, ident, parent):
100 def execute_request(self, ident, parent):
101 try:
101 try:
102 code = parent[u'content'][u'code']
102 code = parent[u'content'][u'code']
103 except:
103 except:
104 print>>sys.__stderr__, "Got bad msg: "
104 print>>sys.__stderr__, "Got bad msg: "
105 print>>sys.__stderr__, Message(parent)
105 print>>sys.__stderr__, Message(parent)
106 return
106 return
107 pyin_msg = self.session.send(self.pub_socket, u'pyin',{u'code':code}, parent=parent)
107 pyin_msg = self.session.send(self.pub_socket, u'pyin',{u'code':code}, parent=parent)
108
108
109 try:
109 try:
110 comp_code = self.compiler(code, '<zmq-kernel>')
110 comp_code = self.compiler(code, '<zmq-kernel>')
111
111
112 # Replace raw_input. Note that is not sufficient to replace
112 # Replace raw_input. Note that is not sufficient to replace
113 # raw_input in the user namespace.
113 # raw_input in the user namespace.
114 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
114 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
115 __builtin__.raw_input = raw_input
115 __builtin__.raw_input = raw_input
116
116
117 # Set the parent message of the display hook and out streams.
117 # Set the parent message of the display hook and out streams.
118 sys.displayhook.set_parent(parent)
118 sys.displayhook.set_parent(parent)
119 sys.stdout.set_parent(parent)
119 sys.stdout.set_parent(parent)
120 sys.stderr.set_parent(parent)
120 sys.stderr.set_parent(parent)
121
121
122 exec comp_code in self.user_ns, self.user_ns
122 exec comp_code in self.user_ns, self.user_ns
123 except:
123 except:
124 etype, evalue, tb = sys.exc_info()
124 etype, evalue, tb = sys.exc_info()
125 tb = traceback.format_exception(etype, evalue, tb)
125 tb = traceback.format_exception(etype, evalue, tb)
126 exc_content = {
126 exc_content = {
127 u'status' : u'error',
127 u'status' : u'error',
128 u'traceback' : tb,
128 u'traceback' : tb,
129 u'ename' : unicode(etype.__name__),
129 u'ename' : unicode(etype.__name__),
130 u'evalue' : unicode(evalue)
130 u'evalue' : unicode(evalue)
131 }
131 }
132 exc_msg = self.session.send(self.pub_socket, u'pyerr', exc_content, parent)
132 exc_msg = self.session.send(self.pub_socket, u'pyerr', exc_content, parent)
133 reply_content = exc_content
133 reply_content = exc_content
134 else:
134 else:
135 reply_content = { 'status' : 'ok', 'payload' : {} }
135 reply_content = { 'status' : 'ok', 'payload' : {} }
136
136
137 # Flush output before sending the reply.
137 # Flush output before sending the reply.
138 sys.stderr.flush()
138 sys.stderr.flush()
139 sys.stdout.flush()
139 sys.stdout.flush()
140
140
141 # Send the reply.
141 # Send the reply.
142 reply_msg = self.session.send(self.reply_socket, u'execute_reply', reply_content, parent, ident=ident)
142 reply_msg = self.session.send(self.reply_socket, u'execute_reply', reply_content, parent, ident=ident)
143 print>>sys.__stdout__, Message(reply_msg)
143 print>>sys.__stdout__, Message(reply_msg)
144 if reply_msg['content']['status'] == u'error':
144 if reply_msg['content']['status'] == u'error':
145 self._abort_queue()
145 self._abort_queue()
146
146
147 def complete_request(self, ident, parent):
147 def complete_request(self, ident, parent):
148 matches = {'matches' : self._complete(parent),
148 matches = {'matches' : self._complete(parent),
149 'status' : 'ok'}
149 'status' : 'ok'}
150 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
150 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
151 matches, parent, ident)
151 matches, parent, ident)
152 print >> sys.__stdout__, completion_msg
152 print >> sys.__stdout__, completion_msg
153
153
154 def object_info_request(self, ident, parent):
154 def object_info_request(self, ident, parent):
155 context = parent['content']['oname'].split('.')
155 context = parent['content']['oname'].split('.')
156 object_info = self._object_info(context)
156 object_info = self._object_info(context)
157 msg = self.session.send(self.reply_socket, 'object_info_reply',
157 msg = self.session.send(self.reply_socket, 'object_info_reply',
158 object_info, parent, ident)
158 object_info, parent, ident)
159 print >> sys.__stdout__, msg
159 print >> sys.__stdout__, msg
160
160
161 def shutdown_request(self, ident, parent):
161 def shutdown_request(self, ident, parent):
162 content = dict(parent['content'])
162 content = dict(parent['content'])
163 msg = self.session.send(self.reply_socket, 'shutdown_reply',
163 msg = self.session.send(self.reply_socket, 'shutdown_reply',
164 content, parent, ident)
164 content, parent, ident)
165 msg = self.session.send(self.pub_socket, 'shutdown_reply',
165 msg = self.session.send(self.pub_socket, 'shutdown_reply',
166 content, parent, ident)
166 content, parent, ident)
167 print >> sys.__stdout__, msg
167 print >> sys.__stdout__, msg
168 time.sleep(0.1)
168 time.sleep(0.1)
169 sys.exit(0)
169 sys.exit(0)
170
170
171 #---------------------------------------------------------------------------
171 #---------------------------------------------------------------------------
172 # Protected interface
172 # Protected interface
173 #---------------------------------------------------------------------------
173 #---------------------------------------------------------------------------
174
174
175 def _abort_queue(self):
175 def _abort_queue(self):
176 while True:
176 while True:
177 try:
177 try:
178 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
178 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
179 except zmq.ZMQError, e:
179 except zmq.ZMQError, e:
180 if e.errno == zmq.EAGAIN:
180 if e.errno == zmq.EAGAIN:
181 break
181 break
182 else:
182 else:
183 assert ident is not None, "Missing message part."
183 assert ident is not None, "Missing message part."
184 print>>sys.__stdout__, "Aborting:"
184 print>>sys.__stdout__, "Aborting:"
185 print>>sys.__stdout__, Message(msg)
185 print>>sys.__stdout__, Message(msg)
186 msg_type = msg['msg_type']
186 msg_type = msg['msg_type']
187 reply_type = msg_type.split('_')[0] + '_reply'
187 reply_type = msg_type.split('_')[0] + '_reply'
188 reply_msg = self.session.send(self.reply_socket, reply_type, {'status':'aborted'}, msg, ident=ident)
188 reply_msg = self.session.send(self.reply_socket, reply_type, {'status':'aborted'}, msg, ident=ident)
189 print>>sys.__stdout__, Message(reply_msg)
189 print>>sys.__stdout__, Message(reply_msg)
190 # We need to wait a bit for requests to come in. This can probably
190 # We need to wait a bit for requests to come in. This can probably
191 # be set shorter for true asynchronous clients.
191 # be set shorter for true asynchronous clients.
192 time.sleep(0.1)
192 time.sleep(0.1)
193
193
194 def _raw_input(self, prompt, ident, parent):
194 def _raw_input(self, prompt, ident, parent):
195 # Flush output before making the request.
195 # Flush output before making the request.
196 sys.stderr.flush()
196 sys.stderr.flush()
197 sys.stdout.flush()
197 sys.stdout.flush()
198
198
199 # Send the input request.
199 # Send the input request.
200 content = dict(prompt=prompt)
200 content = dict(prompt=prompt)
201 msg = self.session.send(self.req_socket, u'input_request', content, parent)
201 msg = self.session.send(self.req_socket, u'input_request', content, parent)
202
202
203 # Await a response.
203 # Await a response.
204 ident,reply = self.session.recv(self.req_socket, 0)
204 ident,reply = self.session.recv(self.req_socket, 0)
205 try:
205 try:
206 value = reply['content']['value']
206 value = reply['content']['value']
207 except:
207 except:
208 print>>sys.__stderr__, "Got bad raw_input reply: "
208 print>>sys.__stderr__, "Got bad raw_input reply: "
209 print>>sys.__stderr__, Message(parent)
209 print>>sys.__stderr__, Message(parent)
210 value = ''
210 value = ''
211 return value
211 return value
212
212
213 def _complete(self, msg):
213 def _complete(self, msg):
214 return self.completer.complete(msg.content.line, msg.content.text)
214 return self.completer.complete(msg.content.line, msg.content.text)
215
215
216 def _object_info(self, context):
216 def _object_info(self, context):
217 symbol, leftover = self._symbol_from_context(context)
217 symbol, leftover = self._symbol_from_context(context)
218 if symbol is not None and not leftover:
218 if symbol is not None and not leftover:
219 doc = getattr(symbol, '__doc__', '')
219 doc = getattr(symbol, '__doc__', '')
220 else:
220 else:
221 doc = ''
221 doc = ''
222 object_info = dict(docstring = doc)
222 object_info = dict(docstring = doc)
223 return object_info
223 return object_info
224
224
225 def _symbol_from_context(self, context):
225 def _symbol_from_context(self, context):
226 if not context:
226 if not context:
227 return None, context
227 return None, context
228
228
229 base_symbol_string = context[0]
229 base_symbol_string = context[0]
230 symbol = self.user_ns.get(base_symbol_string, None)
230 symbol = self.user_ns.get(base_symbol_string, None)
231 if symbol is None:
231 if symbol is None:
232 symbol = __builtin__.__dict__.get(base_symbol_string, None)
232 symbol = __builtin__.__dict__.get(base_symbol_string, None)
233 if symbol is None:
233 if symbol is None:
234 return None, context
234 return None, context
235
235
236 context = context[1:]
236 context = context[1:]
237 for i, name in enumerate(context):
237 for i, name in enumerate(context):
238 new_symbol = getattr(symbol, name, None)
238 new_symbol = getattr(symbol, name, None)
239 if new_symbol is None:
239 if new_symbol is None:
240 return symbol, context[i:]
240 return symbol, context[i:]
241 else:
241 else:
242 symbol = new_symbol
242 symbol = new_symbol
243
243
244 return symbol, []
244 return symbol, []
245
245
246 #-----------------------------------------------------------------------------
246 #-----------------------------------------------------------------------------
247 # Kernel main and launch functions
247 # Kernel main and launch functions
248 #-----------------------------------------------------------------------------
248 #-----------------------------------------------------------------------------
249
249
250 def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
250 def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
251 independent=False):
251 executable=None, independent=False):
252 """ Launches a localhost kernel, binding to the specified ports.
252 """ Launches a localhost kernel, binding to the specified ports.
253
253
254 Parameters
254 Parameters
255 ----------
255 ----------
256 ip : str, optional
256 ip : str, optional
257 The ip address the kernel will bind to.
257 The ip address the kernel will bind to.
258
258
259 xrep_port : int, optional
259 xrep_port : int, optional
260 The port to use for XREP channel.
260 The port to use for XREP channel.
261
261
262 pub_port : int, optional
262 pub_port : int, optional
263 The port to use for the SUB channel.
263 The port to use for the SUB channel.
264
264
265 req_port : int, optional
265 req_port : int, optional
266 The port to use for the REQ (raw input) channel.
266 The port to use for the REQ (raw input) channel.
267
267
268 hb_port : int, optional
268 hb_port : int, optional
269 The port to use for the hearbeat REP channel.
269 The port to use for the hearbeat REP channel.
270
270
271 executable : str, optional (default sys.executable)
272 The Python executable to use for the kernel process.
273
271 independent : bool, optional (default False)
274 independent : bool, optional (default False)
272 If set, the kernel process is guaranteed to survive if this process
275 If set, the kernel process is guaranteed to survive if this process
273 dies. If not set, an effort is made to ensure that the kernel is killed
276 dies. If not set, an effort is made to ensure that the kernel is killed
274 when this process dies. Note that in this case it is still good practice
277 when this process dies. Note that in this case it is still good practice
275 to kill kernels manually before exiting.
278 to kill kernels manually before exiting.
276
279
277 Returns
280 Returns
278 -------
281 -------
279 A tuple of form:
282 A tuple of form:
280 (kernel_process, xrep_port, pub_port, req_port)
283 (kernel_process, xrep_port, pub_port, req_port)
281 where kernel_process is a Popen object and the ports are integers.
284 where kernel_process is a Popen object and the ports are integers.
282 """
285 """
283 extra_arguments = []
286 extra_arguments = []
284 if ip is not None:
287 if ip is not None:
285 extra_arguments.append('--ip')
288 extra_arguments.append('--ip')
286 if isinstance(ip, basestring):
289 if isinstance(ip, basestring):
287 extra_arguments.append(ip)
290 extra_arguments.append(ip)
288
291
289 return base_launch_kernel('from IPython.zmq.pykernel import main; main()',
292 return base_launch_kernel('from IPython.zmq.pykernel import main; main()',
290 xrep_port, pub_port, req_port, hb_port,
293 xrep_port, pub_port, req_port, hb_port,
291 independent, extra_arguments=extra_arguments)
294 executable, independent,
295 extra_arguments=extra_arguments)
292
296
293 main = make_default_main(Kernel)
297 main = make_default_main(Kernel)
294
298
295 if __name__ == '__main__':
299 if __name__ == '__main__':
296 main()
300 main()
General Comments 0
You need to be logged in to leave comments. Login now