##// END OF EJS Templates
zmq kernels now started via newapp
MinRK -
Show More
@@ -0,0 +1,213 b''
1 #!/usr/bin/env python
2 """An Application for launching a kernel
3
4 Authors
5 -------
6 * MinRK
7 """
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2011 The IPython Development Team
10 #
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING.txt, distributed as part of this software.
13 #-----------------------------------------------------------------------------
14
15 #-----------------------------------------------------------------------------
16 # Imports
17 #-----------------------------------------------------------------------------
18
19 # Standard library imports.
20 import os
21 import sys
22
23 # System library imports.
24 import zmq
25
26 # IPython imports.
27 from IPython.core.ultratb import FormattedTB
28 from IPython.core.newapplication import (
29 BaseIPythonApplication, base_flags, base_aliases
30 )
31 from IPython.utils import io
32 from IPython.utils.localinterfaces import LOCALHOST
33 from IPython.utils.traitlets import Any, Instance, Dict, Unicode, Int, Bool
34 from IPython.utils.importstring import import_item
35 # local imports
36 from IPython.zmq.heartbeat import Heartbeat
37 from IPython.zmq.parentpoller import ParentPollerUnix, ParentPollerWindows
38 from IPython.zmq.session import Session
39
40
41 #-----------------------------------------------------------------------------
42 # Flags and Aliases
43 #-----------------------------------------------------------------------------
44
45 kernel_aliases = dict(base_aliases)
46 kernel_aliases.update({
47 'ip' : 'KernelApp.ip',
48 'hb' : 'KernelApp.hb_port',
49 'shell' : 'KernelApp.shell_port',
50 'iopub' : 'KernelApp.iopub_port',
51 'stdin' : 'KernelApp.stdin_port',
52 'parent': 'KernelApp.parent',
53 })
54 if sys.platform.startswith('win'):
55 kernel_aliases['interrupt'] = 'KernelApp.interrupt'
56
57 kernel_flags = dict(base_flags)
58 kernel_flags.update({
59 'no-stdout' : (
60 {'KernelApp' : {'no_stdout' : True}},
61 "redirect stdout to the null device"),
62 'no-stderr' : (
63 {'KernelApp' : {'no_stderr' : True}},
64 "redirect stderr to the null device"),
65 })
66
67
68 #-----------------------------------------------------------------------------
69 # Application class for starting a Kernel
70 #-----------------------------------------------------------------------------
71
72 class KernelApp(BaseIPythonApplication):
73 name='pykernel'
74 aliases = Dict(kernel_aliases)
75 flags = Dict(kernel_flags)
76
77 # the kernel class, as an importstring
78 kernel_class = Unicode('IPython.zmq.pykernel.Kernel')
79 kernel = Any()
80 poller = Any() # don't restrict this even though current pollers are all Threads
81 heartbeat = Instance(Heartbeat)
82 session = Instance('IPython.zmq.session.Session')
83 ports = Dict()
84
85 # connection info:
86 ip = Unicode(LOCALHOST, config=True,
87 help="Set the IP or interface on which the kernel will listen.")
88 hb_port = Int(0, config=True, help="set the heartbeat port [default: random]")
89 shell_port = Int(0, config=True, help="set the shell (XREP) port [default: random]")
90 iopub_port = Int(0, config=True, help="set the iopub (PUB) port [default: random]")
91 stdin_port = Int(0, config=True, help="set the stdin (XREQ) port [default: random]")
92
93 # streams, etc.
94 no_stdout = Bool(False, config=True, help="redirect stdout to the null device")
95 no_stderr = Bool(False, config=True, help="redirect stderr to the null device")
96 outstream_class = Unicode('IPython.zmq.iostream.OutStream', config=True,
97 help="The importstring for the OutStream factory")
98 displayhook_class = Unicode('IPython.zmq.displayhook.DisplayHook', config=True,
99 help="The importstring for the DisplayHook factory")
100
101 # polling
102 parent = Int(0, config=True,
103 help="""kill this process if its parent dies. On Windows, the argument
104 specifies the HANDLE of the parent process, otherwise it is simply boolean.
105 """)
106 interrupt = Int(0, config=True,
107 help="""ONLY USED ON WINDOWS
108 Interrupt this process when the parent is signalled.
109 """)
110
111 def init_crash_handler(self):
112 # Install minimal exception handling
113 sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor',
114 ostream=sys.__stdout__)
115
116 def init_poller(self):
117 if sys.platform == 'win32':
118 if self.interrupt or self.parent:
119 self.poller = ParentPollerWindows(self.interrupt, self.parent)
120 elif self.parent:
121 self.poller = ParentPollerUnix()
122
123 def _bind_socket(self, s, port):
124 iface = 'tcp://%s' % self.ip
125 if port <= 0:
126 port = s.bind_to_random_port(iface)
127 else:
128 s.bind(iface + ':%i'%port)
129 return port
130
131 def init_sockets(self):
132 # Create a context, a session, and the kernel sockets.
133 io.raw_print("Starting the kernel at pid:", os.getpid())
134 context = zmq.Context.instance()
135 # Uncomment this to try closing the context.
136 # atexit.register(context.term)
137
138 self.shell_socket = context.socket(zmq.XREP)
139 self.shell_port = self._bind_socket(self.shell_socket, self.shell_port)
140 self.log.debug("shell XREP Channel on port: %i"%self.shell_port)
141
142 self.iopub_socket = context.socket(zmq.PUB)
143 self.iopub_port = self._bind_socket(self.iopub_socket, self.iopub_port)
144 self.log.debug("iopub PUB Channel on port: %i"%self.iopub_port)
145
146 self.stdin_socket = context.socket(zmq.XREQ)
147 self.stdin_port = self._bind_socket(self.stdin_socket, self.stdin_port)
148 self.log.debug("stdin XREQ Channel on port: %i"%self.stdin_port)
149
150 self.heartbeat = Heartbeat(context, (self.ip, self.hb_port))
151 self.hb_port = self.heartbeat.port
152 self.log.debug("Heartbeat REP Channel on port: %i"%self.hb_port)
153
154 # Helper to make it easier to connect to an existing kernel, until we have
155 # single-port connection negotiation fully implemented.
156 self.log.info("To connect another client to this kernel, use:")
157 self.log.info("--external shell={0} iopub={1} stdin={2} hb={3}".format(
158 self.shell_port, self.iopub_port, self.stdin_port, self.hb_port))
159
160
161 self.ports = dict(shell=self.shell_port, iopub=self.iopub_port,
162 stdin=self.stdin_port, hb=self.hb_port)
163
164 def init_session(self):
165 """create our session object"""
166 self.session = Session(username=u'kernel')
167
168 def init_io(self):
169 """redirects stdout/stderr, and installs a display hook"""
170 # Re-direct stdout/stderr, if necessary.
171 if self.no_stdout or self.no_stderr:
172 blackhole = file(os.devnull, 'w')
173 if self.no_stdout:
174 sys.stdout = sys.__stdout__ = blackhole
175 if self.no_stderr:
176 sys.stderr = sys.__stderr__ = blackhole
177
178 # Redirect input streams and set a display hook.
179
180 if self.outstream_class:
181 outstream_factory = import_item(str(self.outstream_class))
182 sys.stdout = outstream_factory(self.session, self.iopub_socket, u'stdout')
183 sys.stderr = outstream_factory(self.session, self.iopub_socket, u'stderr')
184 if self.displayhook_class:
185 displayhook_factory = import_item(str(self.displayhook_class))
186 sys.displayhook = displayhook_factory(self.session, self.iopub_socket)
187
188 def init_kernel(self):
189 """Create the Kernel object itself"""
190 kernel_factory = import_item(str(self.kernel_class))
191 self.kernel = kernel_factory(config=self.config, session=self.session,
192 shell_socket=self.shell_socket,
193 iopub_socket=self.iopub_socket,
194 stdin_socket=self.stdin_socket,
195 )
196 self.kernel.record_ports(self.ports)
197
198 def initialize(self, argv=None):
199 super(KernelApp, self).initialize(argv)
200 self.init_session()
201 self.init_poller()
202 self.init_sockets()
203 self.init_io()
204 self.init_kernel()
205
206 def start(self):
207 self.heartbeat.start()
208 if self.poller is not None:
209 self.poller.start()
210 try:
211 self.kernel.start()
212 except KeyboardInterrupt:
213 pass
@@ -0,0 +1,13 b''
1 =======================
2 Log Topic Specification
3 =======================
4
5 we use pyzmq to broadcast log events over a PUB socket. Engines, Controllers, etc. can all
6 broadcast. SUB sockets can be used to view the logs, and ZMQ topics are used to help
7 select out what to follow.
8
9 the PUBHandler object that emits the logs can ascribe topics to log messages. The order is:
10
11 <root_topic>.<loglevel>.<subtopic>[.<etc>]
12
13 root_topic is specified as an attribute
@@ -0,0 +1,111 b''
1 """test building messages with streamsession"""
2
3 #-------------------------------------------------------------------------------
4 # Copyright (C) 2011 The IPython Development Team
5 #
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
8 #-------------------------------------------------------------------------------
9
10 #-------------------------------------------------------------------------------
11 # Imports
12 #-------------------------------------------------------------------------------
13
14 import os
15 import uuid
16 import zmq
17
18 from zmq.tests import BaseZMQTestCase
19 from zmq.eventloop.zmqstream import ZMQStream
20 # from IPython.zmq.tests import SessionTestCase
21 from IPython.parallel import streamsession as ss
22
23 class SessionTestCase(BaseZMQTestCase):
24
25 def setUp(self):
26 BaseZMQTestCase.setUp(self)
27 self.session = ss.StreamSession()
28
29 class TestSession(SessionTestCase):
30
31 def test_msg(self):
32 """message format"""
33 msg = self.session.msg('execute')
34 thekeys = set('header msg_id parent_header msg_type content'.split())
35 s = set(msg.keys())
36 self.assertEquals(s, thekeys)
37 self.assertTrue(isinstance(msg['content'],dict))
38 self.assertTrue(isinstance(msg['header'],dict))
39 self.assertTrue(isinstance(msg['parent_header'],dict))
40 self.assertEquals(msg['msg_type'], 'execute')
41
42
43
44 def test_args(self):
45 """initialization arguments for StreamSession"""
46 s = self.session
47 self.assertTrue(s.pack is ss.default_packer)
48 self.assertTrue(s.unpack is ss.default_unpacker)
49 self.assertEquals(s.username, os.environ.get('USER', 'username'))
50
51 s = ss.StreamSession()
52 self.assertEquals(s.username, os.environ.get('USER', 'username'))
53
54 self.assertRaises(TypeError, ss.StreamSession, pack='hi')
55 self.assertRaises(TypeError, ss.StreamSession, unpack='hi')
56 u = str(uuid.uuid4())
57 s = ss.StreamSession(username='carrot', session=u)
58 self.assertEquals(s.session, u)
59 self.assertEquals(s.username, 'carrot')
60
61 def test_tracking(self):
62 """test tracking messages"""
63 a,b = self.create_bound_pair(zmq.PAIR, zmq.PAIR)
64 s = self.session
65 stream = ZMQStream(a)
66 msg = s.send(a, 'hello', track=False)
67 self.assertTrue(msg['tracker'] is None)
68 msg = s.send(a, 'hello', track=True)
69 self.assertTrue(isinstance(msg['tracker'], zmq.MessageTracker))
70 M = zmq.Message(b'hi there', track=True)
71 msg = s.send(a, 'hello', buffers=[M], track=True)
72 t = msg['tracker']
73 self.assertTrue(isinstance(t, zmq.MessageTracker))
74 self.assertRaises(zmq.NotDone, t.wait, .1)
75 del M
76 t.wait(1) # this will raise
77
78
79 # def test_rekey(self):
80 # """rekeying dict around json str keys"""
81 # d = {'0': uuid.uuid4(), 0:uuid.uuid4()}
82 # self.assertRaises(KeyError, ss.rekey, d)
83 #
84 # d = {'0': uuid.uuid4(), 1:uuid.uuid4(), 'asdf':uuid.uuid4()}
85 # d2 = {0:d['0'],1:d[1],'asdf':d['asdf']}
86 # rd = ss.rekey(d)
87 # self.assertEquals(d2,rd)
88 #
89 # d = {'1.5':uuid.uuid4(),'1':uuid.uuid4()}
90 # d2 = {1.5:d['1.5'],1:d['1']}
91 # rd = ss.rekey(d)
92 # self.assertEquals(d2,rd)
93 #
94 # d = {'1.0':uuid.uuid4(),'1':uuid.uuid4()}
95 # self.assertRaises(KeyError, ss.rekey, d)
96 #
97 def test_unique_msg_ids(self):
98 """test that messages receive unique ids"""
99 ids = set()
100 for i in range(2**12):
101 h = self.session.msg_header('test')
102 msg_id = h['msg_id']
103 self.assertTrue(msg_id not in ids)
104 ids.add(msg_id)
105
106 def test_feed_identities(self):
107 """scrub the front for zmq IDENTITIES"""
108 theids = "engine client other".split()
109 content = dict(code='whoda',stuff=object())
110 themsg = self.session.msg('execute',content=content)
111 pmsg = theids
@@ -217,9 +217,12 b' def main():'
217 217 if args.pure:
218 218 kwargs['ipython']=False
219 219 else:
220 kwargs['colors']=colors
220 extra = []
221 if colors:
222 extra.append("colors=%s"%colors)
221 223 if args.pylab:
222 kwargs['pylab']=args.pylab
224 extra.append("pylab=%s"%args.pylab)
225 kwargs['extra_arguments'] = extra
223 226
224 227 kernel_manager.start_kernel(**kwargs)
225 228 kernel_manager.start_channels()
@@ -9,159 +9,14 b' import socket'
9 9 from subprocess import Popen, PIPE
10 10 import sys
11 11
12 # System library imports.
13 import zmq
14
15 12 # Local imports.
16 from IPython.core.ultratb import FormattedTB
17 from IPython.external.argparse import ArgumentParser
18 from IPython.utils import io
19 from IPython.utils.localinterfaces import LOCALHOST
20 from displayhook import DisplayHook
21 from heartbeat import Heartbeat
22 from iostream import OutStream
23 from parentpoller import ParentPollerUnix, ParentPollerWindows
24 from session import Session
25
26
27 def bind_port(socket, ip, port):
28 """ Binds the specified ZMQ socket. If the port is zero, a random port is
29 chosen. Returns the port that was bound.
30 """
31 connection = 'tcp://%s' % ip
32 if port <= 0:
33 port = socket.bind_to_random_port(connection)
34 else:
35 connection += ':%i' % port
36 socket.bind(connection)
37 return port
38
39
40 def make_argument_parser():
41 """ Creates an ArgumentParser for the generic arguments supported by all
42 kernel entry points.
43 """
44 parser = ArgumentParser()
45 parser.add_argument('--ip', type=str, default=LOCALHOST,
46 help='set the kernel\'s IP address [default: local]')
47 parser.add_argument('--xrep', type=int, metavar='PORT', default=0,
48 help='set the XREP channel port [default: random]')
49 parser.add_argument('--pub', type=int, metavar='PORT', default=0,
50 help='set the PUB channel port [default: random]')
51 parser.add_argument('--req', type=int, metavar='PORT', default=0,
52 help='set the REQ channel port [default: random]')
53 parser.add_argument('--hb', type=int, metavar='PORT', default=0,
54 help='set the heartbeat port [default: random]')
55 parser.add_argument('--no-stdout', action='store_true',
56 help='redirect stdout to the null device')
57 parser.add_argument('--no-stderr', action='store_true',
58 help='redirect stderr to the null device')
59
60 if sys.platform == 'win32':
61 parser.add_argument('--interrupt', type=int, metavar='HANDLE',
62 default=0, help='interrupt this process when '
63 'HANDLE is signaled')
64 parser.add_argument('--parent', type=int, metavar='HANDLE',
65 default=0, help='kill this process if the process '
66 'with HANDLE dies')
67 else:
68 parser.add_argument('--parent', action='store_true',
69 help='kill this process if its parent dies')
70
71 return parser
72
73
74 def make_kernel(namespace, kernel_factory,
75 out_stream_factory=None, display_hook_factory=None):
76 """ Creates a kernel, redirects stdout/stderr, and installs a display hook
77 and exception handler.
78 """
79 # Re-direct stdout/stderr, if necessary.
80 if namespace.no_stdout or namespace.no_stderr:
81 blackhole = file(os.devnull, 'w')
82 if namespace.no_stdout:
83 sys.stdout = sys.__stdout__ = blackhole
84 if namespace.no_stderr:
85 sys.stderr = sys.__stderr__ = blackhole
86
87 # Install minimal exception handling
88 sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor',
89 ostream=sys.__stdout__)
13 from parentpoller import ParentPollerWindows
90 14
91 # Create a context, a session, and the kernel sockets.
92 io.raw_print("Starting the kernel at pid:", os.getpid())
93 context = zmq.Context()
94 # Uncomment this to try closing the context.
95 # atexit.register(context.close)
96 session = Session(username=u'kernel')
97 15
98 reply_socket = context.socket(zmq.XREP)
99 xrep_port = bind_port(reply_socket, namespace.ip, namespace.xrep)
100 io.raw_print("XREP Channel on port", xrep_port)
101 16
102 pub_socket = context.socket(zmq.PUB)
103 pub_port = bind_port(pub_socket, namespace.ip, namespace.pub)
104 io.raw_print("PUB Channel on port", pub_port)
105
106 req_socket = context.socket(zmq.XREQ)
107 req_port = bind_port(req_socket, namespace.ip, namespace.req)
108 io.raw_print("REQ Channel on port", req_port)
109
110 hb = Heartbeat(context, (namespace.ip, namespace.hb))
111 hb.start()
112 hb_port = hb.port
113 io.raw_print("Heartbeat REP Channel on port", hb_port)
114
115 # Helper to make it easier to connect to an existing kernel, until we have
116 # single-port connection negotiation fully implemented.
117 io.raw_print("To connect another client to this kernel, use:")
118 io.raw_print("-e --xreq {0} --sub {1} --rep {2} --hb {3}".format(
119 xrep_port, pub_port, req_port, hb_port))
120
121 # Redirect input streams and set a display hook.
122 if out_stream_factory:
123 sys.stdout = out_stream_factory(session, pub_socket, u'stdout')
124 sys.stderr = out_stream_factory(session, pub_socket, u'stderr')
125 if display_hook_factory:
126 sys.displayhook = display_hook_factory(session, pub_socket)
127
128 # Create the kernel.
129 kernel = kernel_factory(session=session, reply_socket=reply_socket,
130 pub_socket=pub_socket, req_socket=req_socket)
131 kernel.record_ports(xrep_port=xrep_port, pub_port=pub_port,
132 req_port=req_port, hb_port=hb_port)
133 return kernel
134
135
136 def start_kernel(namespace, kernel):
137 """ Starts a kernel.
138 """
139 # Configure this kernel process to poll the parent process, if necessary.
140 if sys.platform == 'win32':
141 if namespace.interrupt or namespace.parent:
142 poller = ParentPollerWindows(namespace.interrupt, namespace.parent)
143 poller.start()
144 elif namespace.parent:
145 poller = ParentPollerUnix()
146 poller.start()
147
148 # Start the kernel mainloop.
149 kernel.start()
150
151
152 def make_default_main(kernel_factory):
153 """ Creates the simplest possible kernel entry point.
154 """
155 def main():
156 namespace = make_argument_parser().parse_args()
157 kernel = make_kernel(namespace, kernel_factory, OutStream, DisplayHook)
158 start_kernel(namespace, kernel)
159 return main
160
161
162 def base_launch_kernel(code, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
163 stdin=None, stdout=None, stderr=None,
164 executable=None, independent=False, extra_arguments=[]):
17 def base_launch_kernel(code, shell_port=0, iopub_port=0, stdin_port=0, hb_port=0,
18 ip=None, stdin=None, stdout=None, stderr=None,
19 executable=None, independent=False, extra_arguments=[]):
165 20 """ Launches a localhost kernel, binding to the specified ports.
166 21
167 22 Parameters
@@ -169,18 +24,21 b' def base_launch_kernel(code, xrep_port=0, pub_port=0, req_port=0, hb_port=0,'
169 24 code : str,
170 25 A string of Python code that imports and executes a kernel entry point.
171 26
172 xrep_port : int, optional
27 shell_port : int, optional
173 28 The port to use for XREP channel.
174 29
175 pub_port : int, optional
30 iopub_port : int, optional
176 31 The port to use for the SUB channel.
177 32
178 req_port : int, optional
33 stdin_port : int, optional
179 34 The port to use for the REQ (raw input) channel.
180 35
181 36 hb_port : int, optional
182 37 The port to use for the hearbeat REP channel.
183 38
39 ip : str, optional
40 The ip address the kernel will bind to.
41
184 42 stdin, stdout, stderr : optional (default None)
185 43 Standards streams, as defined in subprocess.Popen.
186 44
@@ -199,13 +57,13 b' def base_launch_kernel(code, xrep_port=0, pub_port=0, req_port=0, hb_port=0,'
199 57 Returns
200 58 -------
201 59 A tuple of form:
202 (kernel_process, xrep_port, pub_port, req_port)
60 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
203 61 where kernel_process is a Popen object and the ports are integers.
204 62 """
205 63 # Find open ports as necessary.
206 64 ports = []
207 ports_needed = int(xrep_port <= 0) + int(pub_port <= 0) + \
208 int(req_port <= 0) + int(hb_port <= 0)
65 ports_needed = int(shell_port <= 0) + int(iopub_port <= 0) + \
66 int(stdin_port <= 0) + int(hb_port <= 0)
209 67 for i in xrange(ports_needed):
210 68 sock = socket.socket()
211 69 sock.bind(('', 0))
@@ -214,28 +72,31 b' def base_launch_kernel(code, xrep_port=0, pub_port=0, req_port=0, hb_port=0,'
214 72 port = sock.getsockname()[1]
215 73 sock.close()
216 74 ports[i] = port
217 if xrep_port <= 0:
218 xrep_port = ports.pop(0)
219 if pub_port <= 0:
220 pub_port = ports.pop(0)
221 if req_port <= 0:
222 req_port = ports.pop(0)
75 if shell_port <= 0:
76 shell_port = ports.pop(0)
77 if iopub_port <= 0:
78 iopub_port = ports.pop(0)
79 if stdin_port <= 0:
80 stdin_port = ports.pop(0)
223 81 if hb_port <= 0:
224 82 hb_port = ports.pop(0)
225 83
226 84 # Build the kernel launch command.
227 85 if executable is None:
228 86 executable = sys.executable
229 arguments = [ executable, '-c', code, '--xrep', str(xrep_port),
230 '--pub', str(pub_port), '--req', str(req_port),
231 '--hb', str(hb_port) ]
87 arguments = [ executable, '-c', code, 'shell=%i'%shell_port,
88 'iopub=%i'%iopub_port, 'stdin=%i'%stdin_port,
89 'hb=%i'%hb_port
90 ]
91 if ip is not None:
92 arguments.append('ip=%s'%ip)
232 93 arguments.extend(extra_arguments)
233 94
234 95 # Spawn a kernel.
235 96 if sys.platform == 'win32':
236 97 # Create a Win32 event for interrupting the kernel.
237 98 interrupt_event = ParentPollerWindows.create_interrupt_event()
238 arguments += [ '--interrupt', str(int(interrupt_event)) ]
99 arguments += [ 'interrupt=%i'%interrupt_event ]
239 100
240 101 # If this process in running on pythonw, stdin, stdout, and stderr are
241 102 # invalid. Popen will fail unless they are suitably redirected. We don't
@@ -273,7 +134,7 b' def base_launch_kernel(code, xrep_port=0, pub_port=0, req_port=0, hb_port=0,'
273 134 handle = DuplicateHandle(pid, pid, pid, 0,
274 135 True, # Inheritable by new processes.
275 136 DUPLICATE_SAME_ACCESS)
276 proc = Popen(arguments + ['--parent', str(int(handle))],
137 proc = Popen(arguments + ['parent=%i'%int(handle)],
277 138 stdin=_stdin, stdout=_stdout, stderr=_stderr)
278 139
279 140 # Attach the interrupt event to the Popen objet so it can be used later.
@@ -293,7 +154,7 b' def base_launch_kernel(code, xrep_port=0, pub_port=0, req_port=0, hb_port=0,'
293 154 proc = Popen(arguments, preexec_fn=lambda: os.setsid(),
294 155 stdin=stdin, stdout=stdout, stderr=stderr)
295 156 else:
296 proc = Popen(arguments + ['--parent'],
157 proc = Popen(arguments + ['parent=1'],
297 158 stdin=stdin, stdout=stdout, stderr=stderr)
298 159
299 return proc, xrep_port, pub_port, req_port, hb_port
160 return proc, shell_port, iopub_port, stdin_port, hb_port
@@ -27,37 +27,21 b' import zmq'
27 27
28 28 # Local imports.
29 29 from IPython.config.configurable import Configurable
30 from IPython.config.application import boolean_flag
31 from IPython.core.newapplication import ProfileDir
30 32 from IPython.utils import io
31 33 from IPython.utils.jsonutil import json_clean
32 34 from IPython.lib import pylabtools
33 from IPython.utils.traitlets import Instance, Float
34 from entry_point import (base_launch_kernel, make_argument_parser, make_kernel,
35 start_kernel)
35 from IPython.utils.traitlets import (
36 List, Instance, Float, Dict, Bool, Int, Unicode, CaselessStrEnum
37 )
38 from entry_point import base_launch_kernel
39 from kernelapp import KernelApp, kernel_flags, kernel_aliases
36 40 from iostream import OutStream
37 41 from session import Session, Message
38 42 from zmqshell import ZMQInteractiveShell
39 43
40 #-----------------------------------------------------------------------------
41 # Globals
42 #-----------------------------------------------------------------------------
43
44 # Module-level logger
45 logger = logging.getLogger(__name__)
46 44
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
49 # handler, see:
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
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
54 # logging level.
55
56 if 0: # dbg - set to 1 to actually see the messages.
57 logger.addHandler(logging.StreamHandler())
58 logger.setLevel(logging.DEBUG)
59
60 # /FIXME
61 45
62 46 #-----------------------------------------------------------------------------
63 47 # Main kernel class
@@ -71,9 +55,10 b' class Kernel(Configurable):'
71 55
72 56 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
73 57 session = Instance(Session)
74 reply_socket = Instance('zmq.Socket')
75 pub_socket = Instance('zmq.Socket')
76 req_socket = Instance('zmq.Socket')
58 shell_socket = Instance('zmq.Socket')
59 iopub_socket = Instance('zmq.Socket')
60 stdin_socket = Instance('zmq.Socket')
61 log = Instance(logging.Logger)
77 62
78 63 # Private interface
79 64
@@ -100,7 +85,8 b' class Kernel(Configurable):'
100 85
101 86 # This is a dict of port number that the kernel is listening on. It is set
102 87 # by record_ports and used by connect_request.
103 _recorded_ports = None
88 _recorded_ports = Dict()
89
104 90
105 91
106 92 def __init__(self, **kwargs):
@@ -111,11 +97,11 b' class Kernel(Configurable):'
111 97 atexit.register(self._at_shutdown)
112 98
113 99 # Initialize the InteractiveShell subclass
114 self.shell = ZMQInteractiveShell.instance()
100 self.shell = ZMQInteractiveShell.instance(config=self.config)
115 101 self.shell.displayhook.session = self.session
116 self.shell.displayhook.pub_socket = self.pub_socket
102 self.shell.displayhook.pub_socket = self.iopub_socket
117 103 self.shell.display_pub.session = self.session
118 self.shell.display_pub.pub_socket = self.pub_socket
104 self.shell.display_pub.pub_socket = self.iopub_socket
119 105
120 106 # TMP - hack while developing
121 107 self.shell._reply_content = None
@@ -131,7 +117,7 b' class Kernel(Configurable):'
131 117 def do_one_iteration(self):
132 118 """Do one iteration of the kernel's evaluation loop.
133 119 """
134 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
120 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
135 121 if msg is None:
136 122 return
137 123
@@ -143,21 +129,20 b' class Kernel(Configurable):'
143 129 # Print some info about this message and leave a '--->' marker, so it's
144 130 # easier to trace visually the message chain when debugging. Each
145 131 # handler prints its message at the end.
146 # Eventually we'll move these from stdout to a logger.
147 logger.debug('\n*** MESSAGE TYPE:'+str(msg['msg_type'])+'***')
148 logger.debug(' Content: '+str(msg['content'])+'\n --->\n ')
132 self.log.debug('\n*** MESSAGE TYPE:'+str(msg['msg_type'])+'***')
133 self.log.debug(' Content: '+str(msg['content'])+'\n --->\n ')
149 134
150 135 # Find and call actual handler for message
151 136 handler = self.handlers.get(msg['msg_type'], None)
152 137 if handler is None:
153 logger.error("UNKNOWN MESSAGE TYPE:" +str(msg))
138 self.log.error("UNKNOWN MESSAGE TYPE:" +str(msg))
154 139 else:
155 140 handler(ident, msg)
156 141
157 142 # Check whether we should exit, in case the incoming message set the
158 143 # exit flag on
159 144 if self.shell.exit_now:
160 logger.debug('\nExiting IPython kernel...')
145 self.log.debug('\nExiting IPython kernel...')
161 146 # We do a normal, clean exit, which allows any actions registered
162 147 # via atexit (such as history saving) to take place.
163 148 sys.exit(0)
@@ -166,26 +151,27 b' class Kernel(Configurable):'
166 151 def start(self):
167 152 """ Start the kernel main loop.
168 153 """
154 poller = zmq.Poller()
155 poller.register(self.shell_socket, zmq.POLLIN)
169 156 while True:
170 157 try:
171 time.sleep(self._poll_interval)
158 # scale by extra factor of 10, because there is no
159 # reason for this to be anything less than ~ 0.1s
160 # since it is a real poller and will respond
161 # to events immediately
162 poller.poll(10*1000*self._poll_interval)
172 163 self.do_one_iteration()
173 164 except KeyboardInterrupt:
174 165 # Ctrl-C shouldn't crash the kernel
175 166 io.raw_print("KeyboardInterrupt caught in kernel")
176 167
177 def record_ports(self, xrep_port, pub_port, req_port, hb_port):
168 def record_ports(self, ports):
178 169 """Record the ports that this kernel is using.
179 170
180 171 The creator of the Kernel instance must call this methods if they
181 172 want the :meth:`connect_request` method to return the port numbers.
182 173 """
183 self._recorded_ports = {
184 'xrep_port' : xrep_port,
185 'pub_port' : pub_port,
186 'req_port' : req_port,
187 'hb_port' : hb_port
188 }
174 self._recorded_ports = ports
189 175
190 176 #---------------------------------------------------------------------------
191 177 # Kernel request handlers
@@ -194,11 +180,11 b' class Kernel(Configurable):'
194 180 def _publish_pyin(self, code, parent):
195 181 """Publish the code request on the pyin stream."""
196 182
197 pyin_msg = self.session.send(self.pub_socket, u'pyin',{u'code':code}, parent=parent)
183 pyin_msg = self.session.send(self.iopub_socket, u'pyin',{u'code':code}, parent=parent)
198 184
199 185 def execute_request(self, ident, parent):
200 186
201 status_msg = self.session.send(self.pub_socket,
187 status_msg = self.session.send(self.iopub_socket,
202 188 u'status',
203 189 {u'execution_state':u'busy'},
204 190 parent=parent
@@ -209,8 +195,8 b' class Kernel(Configurable):'
209 195 code = content[u'code']
210 196 silent = content[u'silent']
211 197 except:
212 logger.error("Got bad msg: ")
213 logger.error(str(Message(parent)))
198 self.log.error("Got bad msg: ")
199 self.log.error(str(Message(parent)))
214 200 return
215 201
216 202 shell = self.shell # we'll need this a lot here
@@ -298,14 +284,14 b' class Kernel(Configurable):'
298 284 time.sleep(self._execute_sleep)
299 285
300 286 # Send the reply.
301 reply_msg = self.session.send(self.reply_socket, u'execute_reply',
287 reply_msg = self.session.send(self.shell_socket, u'execute_reply',
302 288 reply_content, parent, ident=ident)
303 logger.debug(str(reply_msg))
289 self.log.debug(str(reply_msg))
304 290
305 291 if reply_msg['content']['status'] == u'error':
306 292 self._abort_queue()
307 293
308 status_msg = self.session.send(self.pub_socket,
294 status_msg = self.session.send(self.iopub_socket,
309 295 u'status',
310 296 {u'execution_state':u'idle'},
311 297 parent=parent
@@ -316,17 +302,17 b' class Kernel(Configurable):'
316 302 matches = {'matches' : matches,
317 303 'matched_text' : txt,
318 304 'status' : 'ok'}
319 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
305 completion_msg = self.session.send(self.shell_socket, 'complete_reply',
320 306 matches, parent, ident)
321 logger.debug(str(completion_msg))
307 self.log.debug(str(completion_msg))
322 308
323 309 def object_info_request(self, ident, parent):
324 310 object_info = self.shell.object_inspect(parent['content']['oname'])
325 311 # Before we send this object over, we scrub it for JSON usage
326 312 oinfo = json_clean(object_info)
327 msg = self.session.send(self.reply_socket, 'object_info_reply',
313 msg = self.session.send(self.shell_socket, 'object_info_reply',
328 314 oinfo, parent, ident)
329 logger.debug(msg)
315 self.log.debug(msg)
330 316
331 317 def history_request(self, ident, parent):
332 318 # We need to pull these out, as passing **kwargs doesn't work with
@@ -353,18 +339,18 b' class Kernel(Configurable):'
353 339 else:
354 340 hist = []
355 341 content = {'history' : list(hist)}
356 msg = self.session.send(self.reply_socket, 'history_reply',
342 msg = self.session.send(self.shell_socket, 'history_reply',
357 343 content, parent, ident)
358 logger.debug(str(msg))
344 self.log.debug(str(msg))
359 345
360 346 def connect_request(self, ident, parent):
361 347 if self._recorded_ports is not None:
362 348 content = self._recorded_ports.copy()
363 349 else:
364 350 content = {}
365 msg = self.session.send(self.reply_socket, 'connect_reply',
351 msg = self.session.send(self.shell_socket, 'connect_reply',
366 352 content, parent, ident)
367 logger.debug(msg)
353 self.log.debug(msg)
368 354
369 355 def shutdown_request(self, ident, parent):
370 356 self.shell.exit_now = True
@@ -377,19 +363,19 b' class Kernel(Configurable):'
377 363
378 364 def _abort_queue(self):
379 365 while True:
380 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
366 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
381 367 if msg is None:
382 368 break
383 369 else:
384 370 assert ident is not None, \
385 371 "Unexpected missing message part."
386 372
387 logger.debug("Aborting:\n"+str(Message(msg)))
373 self.log.debug("Aborting:\n"+str(Message(msg)))
388 374 msg_type = msg['msg_type']
389 375 reply_type = msg_type.split('_')[0] + '_reply'
390 reply_msg = self.session.send(self.reply_socket, reply_type,
376 reply_msg = self.session.send(self.shell_socket, reply_type,
391 377 {'status' : 'aborted'}, msg, ident=ident)
392 logger.debug(reply_msg)
378 self.log.debug(reply_msg)
393 379 # We need to wait a bit for requests to come in. This can probably
394 380 # be set shorter for true asynchronous clients.
395 381 time.sleep(0.1)
@@ -401,15 +387,15 b' class Kernel(Configurable):'
401 387
402 388 # Send the input request.
403 389 content = dict(prompt=prompt)
404 msg = self.session.send(self.req_socket, u'input_request', content, parent)
390 msg = self.session.send(self.stdin_socket, u'input_request', content, parent)
405 391
406 392 # Await a response.
407 ident, reply = self.session.recv(self.req_socket, 0)
393 ident, reply = self.session.recv(self.stdin_socket, 0)
408 394 try:
409 395 value = reply['content']['value']
410 396 except:
411 logger.error("Got bad raw_input reply: ")
412 logger.error(str(Message(parent)))
397 self.log.error("Got bad raw_input reply: ")
398 self.log.error(str(Message(parent)))
413 399 value = ''
414 400 return value
415 401
@@ -461,9 +447,9 b' class Kernel(Configurable):'
461 447 """
462 448 # io.rprint("Kernel at_shutdown") # dbg
463 449 if self._shutdown_message is not None:
464 self.session.send(self.reply_socket, self._shutdown_message)
465 self.session.send(self.pub_socket, self._shutdown_message)
466 logger.debug(str(self._shutdown_message))
450 self.session.send(self.shell_socket, self._shutdown_message)
451 self.session.send(self.iopub_socket, self._shutdown_message)
452 self.log.debug(str(self._shutdown_message))
467 453 # A very short sleep to give zmq time to flush its message buffers
468 454 # before Python truly shuts down.
469 455 time.sleep(0.01)
@@ -569,120 +555,191 b' class GTKKernel(Kernel):'
569 555
570 556
571 557 #-----------------------------------------------------------------------------
572 # Kernel main and launch functions
558 # Aliases and Flags for the IPKernelApp
573 559 #-----------------------------------------------------------------------------
574 560
575 def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
576 stdin=None, stdout=None, stderr=None,
577 executable=None, independent=False, pylab=False, colors=None):
578 """Launches a localhost kernel, binding to the specified ports.
561 flags = dict(kernel_flags)
562
563 addflag = lambda *args: flags.update(boolean_flag(*args))
564 addflag('automagic', 'InteractiveShell.automagic',
565 """Turn on the auto calling of magic commands. Type %%magic at the
566 IPython prompt for more information.""",
567 'Turn off the auto calling of magic commands.'
568 )
569 addflag('banner', 'InteractiveShell.display_banner',
570 "Display a banner upon starting IPython.",
571 "Don't display a banner upon starting IPython."
572 )
573 addflag('pdb', 'InteractiveShell.pdb',
574 "Enable auto calling the pdb debugger after every exception.",
575 "Disable auto calling the pdb debugger after every exception."
576 )
577 addflag('pprint', 'PlainTextFormatter.pprint',
578 "Enable auto pretty printing of results.",
579 "Disable auto auto pretty printing of results."
580 )
581 addflag('color-info', 'InteractiveShell.color_info',
582 """IPython can display information about objects via a set of func-
583 tions, and optionally can use colors for this, syntax highlighting
584 source code and various other elements. However, because this
585 information is passed through a pager (like 'less') and many pagers get
586 confused with color codes, this option is off by default. You can test
587 it and turn it on permanently in your ipython_config.py file if it
588 works for you. Test it and turn it on permanently if it works with
589 your system. The magic function %%color_info allows you to toggle this
590 inter- actively for testing.""",
591 "Disable using colors for info related things."
592 )
593 addflag('deep-reload', 'InteractiveShell.deep_reload',
594 """Enable deep (recursive) reloading by default. IPython can use the
595 deep_reload module which reloads changes in modules recursively (it
596 replaces the reload() function, so you don't need to change anything to
597 use it). deep_reload() forces a full reload of modules whose code may
598 have changed, which the default reload() function does not. When
599 deep_reload is off, IPython will use the normal reload(), but
600 deep_reload will still be available as dreload(). This fea- ture is off
601 by default [which means that you have both normal reload() and
602 dreload()].""",
603 "Disable deep (recursive) reloading by default."
604 )
605 addflag('readline', 'InteractiveShell.readline_use',
606 "Enable readline for command line usage.",
607 "Disable readline for command line usage."
608 )
609
610 flags['pylab'] = (
611 {'IPKernelApp' : {'pylab' : 'auto'}},
612 """Pre-load matplotlib and numpy for interactive use with
613 the default matplotlib backend."""
614 )
615
616 aliases = dict(kernel_aliases)
617
618 # it's possible we don't want short aliases for *all* of these:
619 aliases.update(dict(
620 autocall='InteractiveShell.autocall',
621 cache_size='InteractiveShell.cache_size',
622 colors='InteractiveShell.colors',
623 logfile='InteractiveShell.logfile',
624 log_append='InteractiveShell.logappend',
625 pi1='InteractiveShell.prompt_in1',
626 pi2='InteractiveShell.prompt_in2',
627 po='InteractiveShell.prompt_out',
628 si='InteractiveShell.separate_in',
629 so='InteractiveShell.separate_out',
630 so2='InteractiveShell.separate_out2',
631 xmode='InteractiveShell.xmode',
632 c='IPKernelApp.code_to_run',
633 ext='IPKernelApp.extra_extension',
634 pylab='IPKernelApp.pylab',
635 ))
579 636
580 Parameters
581 ----------
582 ip : str, optional
583 The ip address the kernel will bind to.
584
585 xrep_port : int, optional
586 The port to use for XREP channel.
637 #-----------------------------------------------------------------------------
638 # The IPKernelApp class
639 #-----------------------------------------------------------------------------
587 640
588 pub_port : int, optional
589 The port to use for the SUB channel.
641 class IPKernelApp(KernelApp):
642 name = 'ipkernel'
643
644 aliases = Dict(aliases)
645 flags = Dict(flags)
646 classes = [Kernel, ZMQInteractiveShell, ProfileDir]
647 # configurables
648 pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'inline', 'auto'],
649 config=True,
650 help="""Pre-load matplotlib and numpy for interactive use,
651 selecting a particular matplotlib backend and loop integration.
652 """
653 )
654 extensions = List(Unicode, config=True,
655 help="A list of dotted module names of IPython extensions to load."
656 )
657 extra_extension = Unicode('', config=True,
658 help="dotted module name of an IPython extension to load."
659 )
660 def _extra_extension_changed(self, name, old, new):
661 if new:
662 # add to self.extensions
663 self.extensions.append(new)
664
665 exec_files = List(Unicode, config=True,
666 help="""List of files to run at IPython startup."""
667 )
668 file_to_run = Unicode('', config=True,
669 help="""A file to be run""")
670 def _file_to_run_changed(self, name, old, new):
671 self.exec_files.append(new)
672
673 exec_lines = List(Unicode, config=True,
674 help="""lines of code to run at IPython startup."""
675 )
676 code_to_run = Unicode('', config=True,
677 help="Execute the given command string."
678 )
679 def _code_to_run_changed(self, name, old, new):
680 self.exec_lines.append(new)
681
682 def init_kernel(self):
683 kernel_factory = Kernel
684
685 kernel_map = {
686 'qt' : QtKernel,
687 'qt4': QtKernel,
688 'inline': Kernel,
689 'osx': TkKernel,
690 'wx' : WxKernel,
691 'tk' : TkKernel,
692 'gtk': GTKKernel,
693 }
590 694
591 req_port : int, optional
592 The port to use for the REQ (raw input) channel.
695 if self.pylab:
696 key = None if self.pylab == 'auto' else self.pylab
697 gui, backend = pylabtools.find_gui_and_backend(key)
698 kernel_factory = kernel_map.get(gui)
699 if kernel_factory is None:
700 raise ValueError('GUI is not supported: %r' % gui)
701 pylabtools.activate_matplotlib(backend)
702
703 kernel = kernel_factory(config=self.config, session=self.session,
704 shell_socket=self.shell_socket,
705 iopub_socket=self.iopub_socket,
706 stdin_socket=self.stdin_socket,
707 log=self.log
708 )
709 self.kernel = kernel
710 kernel.record_ports(self.ports)
593 711
594 hb_port : int, optional
595 The port to use for the hearbeat REP channel.
712 if self.pylab:
713 pylabtools.import_pylab(kernel.shell.user_ns, backend,
714 shell=kernel.shell)
596 715
597 stdin, stdout, stderr : optional (default None)
598 Standards streams, as defined in subprocess.Popen.
599 716
600 executable : str, optional (default sys.executable)
601 The Python executable to use for the kernel process.
602 717
603 independent : bool, optional (default False)
604 If set, the kernel process is guaranteed to survive if this process
605 dies. If not set, an effort is made to ensure that the kernel is killed
606 when this process dies. Note that in this case it is still good practice
607 to kill kernels manually before exiting.
718 #-----------------------------------------------------------------------------
719 # Kernel main and launch functions
720 #-----------------------------------------------------------------------------
608 721
609 pylab : bool or string, optional (default False)
610 If not False, the kernel will be launched with pylab enabled. If a
611 string is passed, matplotlib will use the specified backend. Otherwise,
612 matplotlib's default backend will be used.
722 def launch_kernel(*args, **kwargs):
723 """Launches a localhost IPython kernel, binding to the specified ports.
613 724
614 colors : None or string, optional (default None)
615 If not None, specify the color scheme. One of (NoColor, LightBG, Linux)
725 This function simply calls entry_point.base_launch_kernel with the right first
726 command to start an ipkernel. See base_launch_kernel for arguments.
616 727
617 728 Returns
618 729 -------
619 730 A tuple of form:
620 (kernel_process, xrep_port, pub_port, req_port)
731 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
621 732 where kernel_process is a Popen object and the ports are integers.
622 733 """
623 extra_arguments = []
624 if pylab:
625 extra_arguments.append('--pylab')
626 if isinstance(pylab, basestring):
627 extra_arguments.append(pylab)
628 if ip is not None:
629 extra_arguments.append('--ip')
630 if isinstance(ip, basestring):
631 extra_arguments.append(ip)
632 if colors is not None:
633 extra_arguments.append('--colors')
634 extra_arguments.append(colors)
635 734 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
636 xrep_port, pub_port, req_port, hb_port,
637 stdin, stdout, stderr,
638 executable, independent, extra_arguments)
735 *args, **kwargs)
639 736
640 737
641 738 def main():
642 """ The IPython kernel main entry point.
643 """
644 parser = make_argument_parser()
645 parser.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
646 const='auto', help = \
647 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
648 given, the GUI backend is matplotlib's, otherwise use one of: \
649 ['tk', 'gtk', 'qt', 'wx', 'osx', 'inline'].")
650 parser.add_argument('--colors',
651 type=str, dest='colors',
652 help="Set the color scheme (NoColor, Linux, and LightBG).",
653 metavar='ZMQInteractiveShell.colors')
654 namespace = parser.parse_args()
655
656 kernel_class = Kernel
657
658 kernel_classes = {
659 'qt' : QtKernel,
660 'qt4': QtKernel,
661 'inline': Kernel,
662 'osx': TkKernel,
663 'wx' : WxKernel,
664 'tk' : TkKernel,
665 'gtk': GTKKernel,
666 }
667 if namespace.pylab:
668 if namespace.pylab == 'auto':
669 gui, backend = pylabtools.find_gui_and_backend()
670 else:
671 gui, backend = pylabtools.find_gui_and_backend(namespace.pylab)
672 kernel_class = kernel_classes.get(gui)
673 if kernel_class is None:
674 raise ValueError('GUI is not supported: %r' % gui)
675 pylabtools.activate_matplotlib(backend)
676 if namespace.colors:
677 ZMQInteractiveShell.colors=namespace.colors
678
679 kernel = make_kernel(namespace, kernel_class, OutStream)
680
681 if namespace.pylab:
682 pylabtools.import_pylab(kernel.shell.user_ns, backend,
683 shell=kernel.shell)
684
685 start_kernel(namespace, kernel)
739 """Run a PyKernel as an application"""
740 app = IPKernelApp()
741 app.initialize()
742 app.start()
686 743
687 744
688 745 if __name__ == '__main__':
@@ -782,8 +782,8 b' class KernelManager(HasTraits):'
782 782 else:
783 783 from pykernel import launch_kernel
784 784 self.kernel, xrep, pub, req, _hb = launch_kernel(
785 xrep_port=xreq[1], pub_port=sub[1],
786 req_port=rep[1], hb_port=hb[1], **kw)
785 shell_port=xreq[1], iopub_port=sub[1],
786 stdin_port=rep[1], hb_port=hb[1], **kw)
787 787 self.xreq_address = (xreq[0], xrep)
788 788 self.sub_address = (sub[0], pub)
789 789 self.rep_address = (rep[0], req)
@@ -25,10 +25,11 b' import traceback'
25 25 import zmq
26 26
27 27 # Local imports.
28 from IPython.utils.traitlets import HasTraits, Instance, Float
28 from IPython.utils.traitlets import HasTraits, Instance, Dict, Float
29 29 from completer import KernelCompleter
30 from entry_point import base_launch_kernel, make_default_main
30 from entry_point import base_launch_kernel
31 31 from session import Session, Message
32 from kernelapp import KernelApp
32 33
33 34 #-----------------------------------------------------------------------------
34 35 # Main kernel class
@@ -49,16 +50,16 b' class Kernel(HasTraits):'
49 50
50 51 # This is a dict of port number that the kernel is listening on. It is set
51 52 # by record_ports and used by connect_request.
52 _recorded_ports = None
53 _recorded_ports = Dict()
53 54
54 55 #---------------------------------------------------------------------------
55 56 # Kernel interface
56 57 #---------------------------------------------------------------------------
57 58
58 59 session = Instance(Session)
59 reply_socket = Instance('zmq.Socket')
60 pub_socket = Instance('zmq.Socket')
61 req_socket = Instance('zmq.Socket')
60 shell_socket = Instance('zmq.Socket')
61 iopub_socket = Instance('zmq.Socket')
62 stdin_socket = Instance('zmq.Socket')
62 63
63 64 def __init__(self, **kwargs):
64 65 super(Kernel, self).__init__(**kwargs)
@@ -78,7 +79,7 b' class Kernel(HasTraits):'
78 79 """ Start the kernel main loop.
79 80 """
80 81 while True:
81 ident,msg = self.session.recv(self.reply_socket,0)
82 ident,msg = self.session.recv(self.shell_socket,0)
82 83 assert ident is not None, "Missing message part."
83 84 omsg = Message(msg)
84 85 print>>sys.__stdout__
@@ -89,18 +90,13 b' class Kernel(HasTraits):'
89 90 else:
90 91 handler(ident, omsg)
91 92
92 def record_ports(self, xrep_port, pub_port, req_port, hb_port):
93 def record_ports(self, ports):
93 94 """Record the ports that this kernel is using.
94 95
95 96 The creator of the Kernel instance must call this methods if they
96 97 want the :meth:`connect_request` method to return the port numbers.
97 98 """
98 self._recorded_ports = {
99 'xrep_port' : xrep_port,
100 'pub_port' : pub_port,
101 'req_port' : req_port,
102 'hb_port' : hb_port
103 }
99 self._recorded_ports = ports
104 100
105 101 #---------------------------------------------------------------------------
106 102 # Kernel request handlers
@@ -113,7 +109,7 b' class Kernel(HasTraits):'
113 109 print>>sys.__stderr__, "Got bad msg: "
114 110 print>>sys.__stderr__, Message(parent)
115 111 return
116 pyin_msg = self.session.send(self.pub_socket, u'pyin',{u'code':code}, parent=parent)
112 pyin_msg = self.session.send(self.iopub_socket, u'pyin',{u'code':code}, parent=parent)
117 113
118 114 try:
119 115 comp_code = self.compiler(code, '<zmq-kernel>')
@@ -138,7 +134,7 b' class Kernel(HasTraits):'
138 134 u'ename' : unicode(etype.__name__),
139 135 u'evalue' : unicode(evalue)
140 136 }
141 exc_msg = self.session.send(self.pub_socket, u'pyerr', exc_content, parent)
137 exc_msg = self.session.send(self.iopub_socket, u'pyerr', exc_content, parent)
142 138 reply_content = exc_content
143 139 else:
144 140 reply_content = { 'status' : 'ok', 'payload' : {} }
@@ -153,7 +149,7 b' class Kernel(HasTraits):'
153 149 time.sleep(self._execute_sleep)
154 150
155 151 # Send the reply.
156 reply_msg = self.session.send(self.reply_socket, u'execute_reply', reply_content, parent, ident=ident)
152 reply_msg = self.session.send(self.shell_socket, u'execute_reply', reply_content, parent, ident=ident)
157 153 print>>sys.__stdout__, Message(reply_msg)
158 154 if reply_msg['content']['status'] == u'error':
159 155 self._abort_queue()
@@ -161,22 +157,22 b' class Kernel(HasTraits):'
161 157 def complete_request(self, ident, parent):
162 158 matches = {'matches' : self._complete(parent),
163 159 'status' : 'ok'}
164 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
160 completion_msg = self.session.send(self.shell_socket, 'complete_reply',
165 161 matches, parent, ident)
166 162 print >> sys.__stdout__, completion_msg
167 163
168 164 def object_info_request(self, ident, parent):
169 165 context = parent['content']['oname'].split('.')
170 166 object_info = self._object_info(context)
171 msg = self.session.send(self.reply_socket, 'object_info_reply',
167 msg = self.session.send(self.shell_socket, 'object_info_reply',
172 168 object_info, parent, ident)
173 169 print >> sys.__stdout__, msg
174 170
175 171 def shutdown_request(self, ident, parent):
176 172 content = dict(parent['content'])
177 msg = self.session.send(self.reply_socket, 'shutdown_reply',
173 msg = self.session.send(self.shell_socket, 'shutdown_reply',
178 174 content, parent, ident)
179 msg = self.session.send(self.pub_socket, 'shutdown_reply',
175 msg = self.session.send(self.iopub_socket, 'shutdown_reply',
180 176 content, parent, ident)
181 177 print >> sys.__stdout__, msg
182 178 time.sleep(0.1)
@@ -197,7 +193,7 b' class Kernel(HasTraits):'
197 193 print>>sys.__stdout__, Message(msg)
198 194 msg_type = msg['msg_type']
199 195 reply_type = msg_type.split('_')[0] + '_reply'
200 reply_msg = self.session.send(self.reply_socket, reply_type, {'status':'aborted'}, msg, ident=ident)
196 reply_msg = self.session.send(self.shell_socket, reply_type, {'status':'aborted'}, msg, ident=ident)
201 197 print>>sys.__stdout__, Message(reply_msg)
202 198 # We need to wait a bit for requests to come in. This can probably
203 199 # be set shorter for true asynchronous clients.
@@ -210,10 +206,10 b' class Kernel(HasTraits):'
210 206
211 207 # Send the input request.
212 208 content = dict(prompt=prompt)
213 msg = self.session.send(self.req_socket, u'input_request', content, parent)
209 msg = self.session.send(self.stdin_socket, u'input_request', content, parent)
214 210
215 211 # Await a response.
216 ident,reply = self.session.recv(self.req_socket, 0)
212 ident,reply = self.session.recv(self.stdin_socket, 0)
217 213 try:
218 214 value = reply['content']['value']
219 215 except:
@@ -259,58 +255,26 b' class Kernel(HasTraits):'
259 255 # Kernel main and launch functions
260 256 #-----------------------------------------------------------------------------
261 257
262 def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
263 stdin=None, stdout=None, stderr=None,
264 executable=None, independent=False):
265 """ Launches a localhost kernel, binding to the specified ports.
266
267 Parameters
268 ----------
269 ip : str, optional
270 The ip address the kernel will bind to.
258 def launch_kernel(*args, **kwargs):
259 """ Launches a simple Python kernel, binding to the specified ports.
271 260
272 xrep_port : int, optional
273 The port to use for XREP channel.
274
275 pub_port : int, optional
276 The port to use for the SUB channel.
277
278 req_port : int, optional
279 The port to use for the REQ (raw input) channel.
280
281 hb_port : int, optional
282 The port to use for the hearbeat REP channel.
283
284 stdin, stdout, stderr : optional (default None)
285 Standards streams, as defined in subprocess.Popen.
286
287 executable : str, optional (default sys.executable)
288 The Python executable to use for the kernel process.
289
290 independent : bool, optional (default False)
291 If set, the kernel process is guaranteed to survive if this process
292 dies. If not set, an effort is made to ensure that the kernel is killed
293 when this process dies. Note that in this case it is still good practice
294 to kill kernels manually before exiting.
261 This function simply calls entry_point.base_launch_kernel with the right first
262 command to start a pykernel. See base_launch_kernel for arguments.
295 263
296 264 Returns
297 265 -------
298 266 A tuple of form:
299 (kernel_process, xrep_port, pub_port, req_port)
267 (kernel_process, xrep_port, pub_port, req_port, hb_port)
300 268 where kernel_process is a Popen object and the ports are integers.
301 269 """
302 extra_arguments = []
303 if ip is not None:
304 extra_arguments.append('--ip')
305 if isinstance(ip, basestring):
306 extra_arguments.append(ip)
307
308 270 return base_launch_kernel('from IPython.zmq.pykernel import main; main()',
309 xrep_port, pub_port, req_port, hb_port,
310 stdin, stdout, stderr,
311 executable, independent, extra_arguments)
271 *args, **kwargs)
312 272
313 main = make_default_main(Kernel)
273 def main():
274 """Run a PyKernel as an application"""
275 app = KernelApp()
276 app.initialize()
277 app.start()
314 278
315 279 if __name__ == '__main__':
316 280 main()
General Comments 0
You need to be logged in to leave comments. Login now