##// 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 if args.pure:
217 if args.pure:
218 kwargs['ipython']=False
218 kwargs['ipython']=False
219 else:
219 else:
220 kwargs['colors']=colors
220 extra = []
221 if colors:
222 extra.append("colors=%s"%colors)
221 if args.pylab:
223 if args.pylab:
222 kwargs['pylab']=args.pylab
224 extra.append("pylab=%s"%args.pylab)
225 kwargs['extra_arguments'] = extra
223
226
224 kernel_manager.start_kernel(**kwargs)
227 kernel_manager.start_kernel(**kwargs)
225 kernel_manager.start_channels()
228 kernel_manager.start_channels()
@@ -9,159 +9,14 b' 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.
13 import zmq
14
15 # Local imports.
12 # Local imports.
16 from IPython.core.ultratb import FormattedTB
13 from parentpoller import ParentPollerWindows
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__)
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)
17 def base_launch_kernel(code, shell_port=0, iopub_port=0, stdin_port=0, hb_port=0,
103 pub_port = bind_port(pub_socket, namespace.ip, namespace.pub)
18 ip=None, stdin=None, stdout=None, stderr=None,
104 io.raw_print("PUB Channel on port", pub_port)
19 executable=None, independent=False, extra_arguments=[]):
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=[]):
165 """ Launches a localhost kernel, binding to the specified ports.
20 """ Launches a localhost kernel, binding to the specified ports.
166
21
167 Parameters
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 code : str,
24 code : str,
170 A string of Python code that imports and executes a kernel entry point.
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 The port to use for XREP channel.
28 The port to use for XREP channel.
174
29
175 pub_port : int, optional
30 iopub_port : int, optional
176 The port to use for the SUB channel.
31 The port to use for the SUB channel.
177
32
178 req_port : int, optional
33 stdin_port : int, optional
179 The port to use for the REQ (raw input) channel.
34 The port to use for the REQ (raw input) channel.
180
35
181 hb_port : int, optional
36 hb_port : int, optional
182 The port to use for the hearbeat REP channel.
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 stdin, stdout, stderr : optional (default None)
42 stdin, stdout, stderr : optional (default None)
185 Standards streams, as defined in subprocess.Popen.
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 Returns
57 Returns
200 -------
58 -------
201 A tuple of form:
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 where kernel_process is a Popen object and the ports are integers.
61 where kernel_process is a Popen object and the ports are integers.
204 """
62 """
205 # Find open ports as necessary.
63 # Find open ports as necessary.
206 ports = []
64 ports = []
207 ports_needed = int(xrep_port <= 0) + int(pub_port <= 0) + \
65 ports_needed = int(shell_port <= 0) + int(iopub_port <= 0) + \
208 int(req_port <= 0) + int(hb_port <= 0)
66 int(stdin_port <= 0) + int(hb_port <= 0)
209 for i in xrange(ports_needed):
67 for i in xrange(ports_needed):
210 sock = socket.socket()
68 sock = socket.socket()
211 sock.bind(('', 0))
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 port = sock.getsockname()[1]
72 port = sock.getsockname()[1]
215 sock.close()
73 sock.close()
216 ports[i] = port
74 ports[i] = port
217 if xrep_port <= 0:
75 if shell_port <= 0:
218 xrep_port = ports.pop(0)
76 shell_port = ports.pop(0)
219 if pub_port <= 0:
77 if iopub_port <= 0:
220 pub_port = ports.pop(0)
78 iopub_port = ports.pop(0)
221 if req_port <= 0:
79 if stdin_port <= 0:
222 req_port = ports.pop(0)
80 stdin_port = ports.pop(0)
223 if hb_port <= 0:
81 if hb_port <= 0:
224 hb_port = ports.pop(0)
82 hb_port = ports.pop(0)
225
83
226 # Build the kernel launch command.
84 # Build the kernel launch command.
227 if executable is None:
85 if executable is None:
228 executable = sys.executable
86 executable = sys.executable
229 arguments = [ executable, '-c', code, '--xrep', str(xrep_port),
87 arguments = [ executable, '-c', code, 'shell=%i'%shell_port,
230 '--pub', str(pub_port), '--req', str(req_port),
88 'iopub=%i'%iopub_port, 'stdin=%i'%stdin_port,
231 '--hb', str(hb_port) ]
89 'hb=%i'%hb_port
90 ]
91 if ip is not None:
92 arguments.append('ip=%s'%ip)
232 arguments.extend(extra_arguments)
93 arguments.extend(extra_arguments)
233
94
234 # Spawn a kernel.
95 # Spawn a kernel.
235 if sys.platform == 'win32':
96 if sys.platform == 'win32':
236 # Create a Win32 event for interrupting the kernel.
97 # Create a Win32 event for interrupting the kernel.
237 interrupt_event = ParentPollerWindows.create_interrupt_event()
98 interrupt_event = ParentPollerWindows.create_interrupt_event()
238 arguments += [ '--interrupt', str(int(interrupt_event)) ]
99 arguments += [ 'interrupt=%i'%interrupt_event ]
239
100
240 # If this process in running on pythonw, stdin, stdout, and stderr are
101 # If this process in running on pythonw, stdin, stdout, and stderr are
241 # invalid. Popen will fail unless they are suitably redirected. We don't
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 handle = DuplicateHandle(pid, pid, pid, 0,
134 handle = DuplicateHandle(pid, pid, pid, 0,
274 True, # Inheritable by new processes.
135 True, # Inheritable by new processes.
275 DUPLICATE_SAME_ACCESS)
136 DUPLICATE_SAME_ACCESS)
276 proc = Popen(arguments + ['--parent', str(int(handle))],
137 proc = Popen(arguments + ['parent=%i'%int(handle)],
277 stdin=_stdin, stdout=_stdout, stderr=_stderr)
138 stdin=_stdin, stdout=_stdout, stderr=_stderr)
278
139
279 # Attach the interrupt event to the Popen objet so it can be used later.
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 proc = Popen(arguments, preexec_fn=lambda: os.setsid(),
154 proc = Popen(arguments, preexec_fn=lambda: os.setsid(),
294 stdin=stdin, stdout=stdout, stderr=stderr)
155 stdin=stdin, stdout=stdout, stderr=stderr)
295 else:
156 else:
296 proc = Popen(arguments + ['--parent'],
157 proc = Popen(arguments + ['parent=1'],
297 stdin=stdin, stdout=stdout, stderr=stderr)
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 # Local imports.
28 # Local imports.
29 from IPython.config.configurable import Configurable
29 from IPython.config.configurable import Configurable
30 from IPython.config.application import boolean_flag
31 from IPython.core.newapplication import ProfileDir
30 from IPython.utils import io
32 from IPython.utils import io
31 from IPython.utils.jsonutil import json_clean
33 from IPython.utils.jsonutil import json_clean
32 from IPython.lib import pylabtools
34 from IPython.lib import pylabtools
33 from IPython.utils.traitlets import Instance, Float
35 from IPython.utils.traitlets import (
34 from entry_point import (base_launch_kernel, make_argument_parser, make_kernel,
36 List, Instance, Float, Dict, Bool, Int, Unicode, CaselessStrEnum
35 start_kernel)
37 )
38 from entry_point import base_launch_kernel
39 from kernelapp import KernelApp, kernel_flags, kernel_aliases
36 from iostream import OutStream
40 from iostream import OutStream
37 from session import Session, Message
41 from session import Session, Message
38 from zmqshell import ZMQInteractiveShell
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 # Main kernel class
47 # Main kernel class
@@ -71,9 +55,10 b' class Kernel(Configurable):'
71
55
72 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
56 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
73 session = Instance(Session)
57 session = Instance(Session)
74 reply_socket = Instance('zmq.Socket')
58 shell_socket = Instance('zmq.Socket')
75 pub_socket = Instance('zmq.Socket')
59 iopub_socket = Instance('zmq.Socket')
76 req_socket = Instance('zmq.Socket')
60 stdin_socket = Instance('zmq.Socket')
61 log = Instance(logging.Logger)
77
62
78 # Private interface
63 # Private interface
79
64
@@ -100,7 +85,8 b' class Kernel(Configurable):'
100
85
101 # This is a dict of port number that the kernel is listening on. It is set
86 # 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.
87 # by record_ports and used by connect_request.
103 _recorded_ports = None
88 _recorded_ports = Dict()
89
104
90
105
91
106 def __init__(self, **kwargs):
92 def __init__(self, **kwargs):
@@ -111,11 +97,11 b' class Kernel(Configurable):'
111 atexit.register(self._at_shutdown)
97 atexit.register(self._at_shutdown)
112
98
113 # Initialize the InteractiveShell subclass
99 # Initialize the InteractiveShell subclass
114 self.shell = ZMQInteractiveShell.instance()
100 self.shell = ZMQInteractiveShell.instance(config=self.config)
115 self.shell.displayhook.session = self.session
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 self.shell.display_pub.session = self.session
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 # TMP - hack while developing
106 # TMP - hack while developing
121 self.shell._reply_content = None
107 self.shell._reply_content = None
@@ -131,7 +117,7 b' class Kernel(Configurable):'
131 def do_one_iteration(self):
117 def do_one_iteration(self):
132 """Do one iteration of the kernel's evaluation loop.
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 if msg is None:
121 if msg is None:
136 return
122 return
137
123
@@ -143,21 +129,20 b' class Kernel(Configurable):'
143 # Print some info about this message and leave a '--->' marker, so it's
129 # Print some info about this message and leave a '--->' marker, so it's
144 # easier to trace visually the message chain when debugging. Each
130 # easier to trace visually the message chain when debugging. Each
145 # handler prints its message at the end.
131 # handler prints its message at the end.
146 # Eventually we'll move these from stdout to a logger.
132 self.log.debug('\n*** MESSAGE TYPE:'+str(msg['msg_type'])+'***')
147 logger.debug('\n*** MESSAGE TYPE:'+str(msg['msg_type'])+'***')
133 self.log.debug(' Content: '+str(msg['content'])+'\n --->\n ')
148 logger.debug(' Content: '+str(msg['content'])+'\n --->\n ')
149
134
150 # Find and call actual handler for message
135 # Find and call actual handler for message
151 handler = self.handlers.get(msg['msg_type'], None)
136 handler = self.handlers.get(msg['msg_type'], None)
152 if handler is None:
137 if handler is None:
153 logger.error("UNKNOWN MESSAGE TYPE:" +str(msg))
138 self.log.error("UNKNOWN MESSAGE TYPE:" +str(msg))
154 else:
139 else:
155 handler(ident, msg)
140 handler(ident, msg)
156
141
157 # Check whether we should exit, in case the incoming message set the
142 # Check whether we should exit, in case the incoming message set the
158 # exit flag on
143 # exit flag on
159 if self.shell.exit_now:
144 if self.shell.exit_now:
160 logger.debug('\nExiting IPython kernel...')
145 self.log.debug('\nExiting IPython kernel...')
161 # We do a normal, clean exit, which allows any actions registered
146 # We do a normal, clean exit, which allows any actions registered
162 # via atexit (such as history saving) to take place.
147 # via atexit (such as history saving) to take place.
163 sys.exit(0)
148 sys.exit(0)
@@ -166,26 +151,27 b' class Kernel(Configurable):'
166 def start(self):
151 def start(self):
167 """ Start the kernel main loop.
152 """ Start the kernel main loop.
168 """
153 """
154 poller = zmq.Poller()
155 poller.register(self.shell_socket, zmq.POLLIN)
169 while True:
156 while True:
170 try:
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 self.do_one_iteration()
163 self.do_one_iteration()
173 except KeyboardInterrupt:
164 except KeyboardInterrupt:
174 # Ctrl-C shouldn't crash the kernel
165 # Ctrl-C shouldn't crash the kernel
175 io.raw_print("KeyboardInterrupt caught in kernel")
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 """Record the ports that this kernel is using.
169 """Record the ports that this kernel is using.
179
170
180 The creator of the Kernel instance must call this methods if they
171 The creator of the Kernel instance must call this methods if they
181 want the :meth:`connect_request` method to return the port numbers.
172 want the :meth:`connect_request` method to return the port numbers.
182 """
173 """
183 self._recorded_ports = {
174 self._recorded_ports = ports
184 'xrep_port' : xrep_port,
185 'pub_port' : pub_port,
186 'req_port' : req_port,
187 'hb_port' : hb_port
188 }
189
175
190 #---------------------------------------------------------------------------
176 #---------------------------------------------------------------------------
191 # Kernel request handlers
177 # Kernel request handlers
@@ -194,11 +180,11 b' class Kernel(Configurable):'
194 def _publish_pyin(self, code, parent):
180 def _publish_pyin(self, code, parent):
195 """Publish the code request on the pyin stream."""
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 def execute_request(self, ident, parent):
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 u'status',
188 u'status',
203 {u'execution_state':u'busy'},
189 {u'execution_state':u'busy'},
204 parent=parent
190 parent=parent
@@ -209,8 +195,8 b' class Kernel(Configurable):'
209 code = content[u'code']
195 code = content[u'code']
210 silent = content[u'silent']
196 silent = content[u'silent']
211 except:
197 except:
212 logger.error("Got bad msg: ")
198 self.log.error("Got bad msg: ")
213 logger.error(str(Message(parent)))
199 self.log.error(str(Message(parent)))
214 return
200 return
215
201
216 shell = self.shell # we'll need this a lot here
202 shell = self.shell # we'll need this a lot here
@@ -298,14 +284,14 b' class Kernel(Configurable):'
298 time.sleep(self._execute_sleep)
284 time.sleep(self._execute_sleep)
299
285
300 # Send the reply.
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 reply_content, parent, ident=ident)
288 reply_content, parent, ident=ident)
303 logger.debug(str(reply_msg))
289 self.log.debug(str(reply_msg))
304
290
305 if reply_msg['content']['status'] == u'error':
291 if reply_msg['content']['status'] == u'error':
306 self._abort_queue()
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 u'status',
295 u'status',
310 {u'execution_state':u'idle'},
296 {u'execution_state':u'idle'},
311 parent=parent
297 parent=parent
@@ -316,17 +302,17 b' class Kernel(Configurable):'
316 matches = {'matches' : matches,
302 matches = {'matches' : matches,
317 'matched_text' : txt,
303 'matched_text' : txt,
318 'status' : 'ok'}
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 matches, parent, ident)
306 matches, parent, ident)
321 logger.debug(str(completion_msg))
307 self.log.debug(str(completion_msg))
322
308
323 def object_info_request(self, ident, parent):
309 def object_info_request(self, ident, parent):
324 object_info = self.shell.object_inspect(parent['content']['oname'])
310 object_info = self.shell.object_inspect(parent['content']['oname'])
325 # Before we send this object over, we scrub it for JSON usage
311 # Before we send this object over, we scrub it for JSON usage
326 oinfo = json_clean(object_info)
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 oinfo, parent, ident)
314 oinfo, parent, ident)
329 logger.debug(msg)
315 self.log.debug(msg)
330
316
331 def history_request(self, ident, parent):
317 def history_request(self, ident, parent):
332 # We need to pull these out, as passing **kwargs doesn't work with
318 # We need to pull these out, as passing **kwargs doesn't work with
@@ -353,18 +339,18 b' class Kernel(Configurable):'
353 else:
339 else:
354 hist = []
340 hist = []
355 content = {'history' : list(hist)}
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 content, parent, ident)
343 content, parent, ident)
358 logger.debug(str(msg))
344 self.log.debug(str(msg))
359
345
360 def connect_request(self, ident, parent):
346 def connect_request(self, ident, parent):
361 if self._recorded_ports is not None:
347 if self._recorded_ports is not None:
362 content = self._recorded_ports.copy()
348 content = self._recorded_ports.copy()
363 else:
349 else:
364 content = {}
350 content = {}
365 msg = self.session.send(self.reply_socket, 'connect_reply',
351 msg = self.session.send(self.shell_socket, 'connect_reply',
366 content, parent, ident)
352 content, parent, ident)
367 logger.debug(msg)
353 self.log.debug(msg)
368
354
369 def shutdown_request(self, ident, parent):
355 def shutdown_request(self, ident, parent):
370 self.shell.exit_now = True
356 self.shell.exit_now = True
@@ -377,19 +363,19 b' class Kernel(Configurable):'
377
363
378 def _abort_queue(self):
364 def _abort_queue(self):
379 while True:
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 if msg is None:
367 if msg is None:
382 break
368 break
383 else:
369 else:
384 assert ident is not None, \
370 assert ident is not None, \
385 "Unexpected missing message part."
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 msg_type = msg['msg_type']
374 msg_type = msg['msg_type']
389 reply_type = msg_type.split('_')[0] + '_reply'
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 {'status' : 'aborted'}, msg, ident=ident)
377 {'status' : 'aborted'}, msg, ident=ident)
392 logger.debug(reply_msg)
378 self.log.debug(reply_msg)
393 # We need to wait a bit for requests to come in. This can probably
379 # We need to wait a bit for requests to come in. This can probably
394 # be set shorter for true asynchronous clients.
380 # be set shorter for true asynchronous clients.
395 time.sleep(0.1)
381 time.sleep(0.1)
@@ -401,15 +387,15 b' class Kernel(Configurable):'
401
387
402 # Send the input request.
388 # Send the input request.
403 content = dict(prompt=prompt)
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 # Await a response.
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 try:
394 try:
409 value = reply['content']['value']
395 value = reply['content']['value']
410 except:
396 except:
411 logger.error("Got bad raw_input reply: ")
397 self.log.error("Got bad raw_input reply: ")
412 logger.error(str(Message(parent)))
398 self.log.error(str(Message(parent)))
413 value = ''
399 value = ''
414 return value
400 return value
415
401
@@ -461,9 +447,9 b' class Kernel(Configurable):'
461 """
447 """
462 # io.rprint("Kernel at_shutdown") # dbg
448 # io.rprint("Kernel at_shutdown") # dbg
463 if self._shutdown_message is not None:
449 if self._shutdown_message is not None:
464 self.session.send(self.reply_socket, self._shutdown_message)
450 self.session.send(self.shell_socket, self._shutdown_message)
465 self.session.send(self.pub_socket, self._shutdown_message)
451 self.session.send(self.iopub_socket, self._shutdown_message)
466 logger.debug(str(self._shutdown_message))
452 self.log.debug(str(self._shutdown_message))
467 # A very short sleep to give zmq time to flush its message buffers
453 # A very short sleep to give zmq time to flush its message buffers
468 # before Python truly shuts down.
454 # before Python truly shuts down.
469 time.sleep(0.01)
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,
561 flags = dict(kernel_flags)
576 stdin=None, stdout=None, stderr=None,
562
577 executable=None, independent=False, pylab=False, colors=None):
563 addflag = lambda *args: flags.update(boolean_flag(*args))
578 """Launches a localhost kernel, binding to the specified ports.
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
637 #-----------------------------------------------------------------------------
581 ----------
638 # The IPKernelApp class
582 ip : str, optional
639 #-----------------------------------------------------------------------------
583 The ip address the kernel will bind to.
584
585 xrep_port : int, optional
586 The port to use for XREP channel.
587
640
588 pub_port : int, optional
641 class IPKernelApp(KernelApp):
589 The port to use for the SUB channel.
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
695 if self.pylab:
592 The port to use for the REQ (raw input) channel.
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
712 if self.pylab:
595 The port to use for the hearbeat REP channel.
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)
718 #-----------------------------------------------------------------------------
604 If set, the kernel process is guaranteed to survive if this process
719 # Kernel main and launch functions
605 dies. If not set, an effort is made to ensure that the kernel is killed
720 #-----------------------------------------------------------------------------
606 when this process dies. Note that in this case it is still good practice
607 to kill kernels manually before exiting.
608
721
609 pylab : bool or string, optional (default False)
722 def launch_kernel(*args, **kwargs):
610 If not False, the kernel will be launched with pylab enabled. If a
723 """Launches a localhost IPython kernel, binding to the specified ports.
611 string is passed, matplotlib will use the specified backend. Otherwise,
612 matplotlib's default backend will be used.
613
724
614 colors : None or string, optional (default None)
725 This function simply calls entry_point.base_launch_kernel with the right first
615 If not None, specify the color scheme. One of (NoColor, LightBG, Linux)
726 command to start an ipkernel. See base_launch_kernel for arguments.
616
727
617 Returns
728 Returns
618 -------
729 -------
619 A tuple of form:
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 where kernel_process is a Popen object and the ports are integers.
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 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
734 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
636 xrep_port, pub_port, req_port, hb_port,
735 *args, **kwargs)
637 stdin, stdout, stderr,
638 executable, independent, extra_arguments)
639
736
640
737
641 def main():
738 def main():
642 """ The IPython kernel main entry point.
739 """Run a PyKernel as an application"""
643 """
740 app = IPKernelApp()
644 parser = make_argument_parser()
741 app.initialize()
645 parser.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
742 app.start()
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)
686
743
687
744
688 if __name__ == '__main__':
745 if __name__ == '__main__':
@@ -782,8 +782,8 b' class KernelManager(HasTraits):'
782 else:
782 else:
783 from pykernel import launch_kernel
783 from pykernel import launch_kernel
784 self.kernel, xrep, pub, req, _hb = launch_kernel(
784 self.kernel, xrep, pub, req, _hb = launch_kernel(
785 xrep_port=xreq[1], pub_port=sub[1],
785 shell_port=xreq[1], iopub_port=sub[1],
786 req_port=rep[1], hb_port=hb[1], **kw)
786 stdin_port=rep[1], hb_port=hb[1], **kw)
787 self.xreq_address = (xreq[0], xrep)
787 self.xreq_address = (xreq[0], xrep)
788 self.sub_address = (sub[0], pub)
788 self.sub_address = (sub[0], pub)
789 self.rep_address = (rep[0], req)
789 self.rep_address = (rep[0], req)
@@ -25,10 +25,11 b' import traceback'
25 import zmq
25 import zmq
26
26
27 # Local imports.
27 # Local imports.
28 from IPython.utils.traitlets import HasTraits, Instance, Float
28 from IPython.utils.traitlets import HasTraits, Instance, Dict, Float
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
31 from session import Session, Message
31 from session import Session, Message
32 from kernelapp import KernelApp
32
33
33 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
34 # Main kernel class
35 # Main kernel class
@@ -49,16 +50,16 b' class Kernel(HasTraits):'
49
50
50 # This is a dict of port number that the kernel is listening on. It is set
51 # This is a dict of port number that the kernel is listening on. It is set
51 # by record_ports and used by connect_request.
52 # by record_ports and used by connect_request.
52 _recorded_ports = None
53 _recorded_ports = Dict()
53
54
54 #---------------------------------------------------------------------------
55 #---------------------------------------------------------------------------
55 # Kernel interface
56 # Kernel interface
56 #---------------------------------------------------------------------------
57 #---------------------------------------------------------------------------
57
58
58 session = Instance(Session)
59 session = Instance(Session)
59 reply_socket = Instance('zmq.Socket')
60 shell_socket = Instance('zmq.Socket')
60 pub_socket = Instance('zmq.Socket')
61 iopub_socket = Instance('zmq.Socket')
61 req_socket = Instance('zmq.Socket')
62 stdin_socket = Instance('zmq.Socket')
62
63
63 def __init__(self, **kwargs):
64 def __init__(self, **kwargs):
64 super(Kernel, self).__init__(**kwargs)
65 super(Kernel, self).__init__(**kwargs)
@@ -78,7 +79,7 b' class Kernel(HasTraits):'
78 """ Start the kernel main loop.
79 """ Start the kernel main loop.
79 """
80 """
80 while True:
81 while True:
81 ident,msg = self.session.recv(self.reply_socket,0)
82 ident,msg = self.session.recv(self.shell_socket,0)
82 assert ident is not None, "Missing message part."
83 assert ident is not None, "Missing message part."
83 omsg = Message(msg)
84 omsg = Message(msg)
84 print>>sys.__stdout__
85 print>>sys.__stdout__
@@ -89,18 +90,13 b' class Kernel(HasTraits):'
89 else:
90 else:
90 handler(ident, omsg)
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 """Record the ports that this kernel is using.
94 """Record the ports that this kernel is using.
94
95
95 The creator of the Kernel instance must call this methods if they
96 The creator of the Kernel instance must call this methods if they
96 want the :meth:`connect_request` method to return the port numbers.
97 want the :meth:`connect_request` method to return the port numbers.
97 """
98 """
98 self._recorded_ports = {
99 self._recorded_ports = ports
99 'xrep_port' : xrep_port,
100 'pub_port' : pub_port,
101 'req_port' : req_port,
102 'hb_port' : hb_port
103 }
104
100
105 #---------------------------------------------------------------------------
101 #---------------------------------------------------------------------------
106 # Kernel request handlers
102 # Kernel request handlers
@@ -113,7 +109,7 b' class Kernel(HasTraits):'
113 print>>sys.__stderr__, "Got bad msg: "
109 print>>sys.__stderr__, "Got bad msg: "
114 print>>sys.__stderr__, Message(parent)
110 print>>sys.__stderr__, Message(parent)
115 return
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 try:
114 try:
119 comp_code = self.compiler(code, '<zmq-kernel>')
115 comp_code = self.compiler(code, '<zmq-kernel>')
@@ -138,7 +134,7 b' class Kernel(HasTraits):'
138 u'ename' : unicode(etype.__name__),
134 u'ename' : unicode(etype.__name__),
139 u'evalue' : unicode(evalue)
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 reply_content = exc_content
138 reply_content = exc_content
143 else:
139 else:
144 reply_content = { 'status' : 'ok', 'payload' : {} }
140 reply_content = { 'status' : 'ok', 'payload' : {} }
@@ -153,7 +149,7 b' class Kernel(HasTraits):'
153 time.sleep(self._execute_sleep)
149 time.sleep(self._execute_sleep)
154
150
155 # Send the reply.
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 print>>sys.__stdout__, Message(reply_msg)
153 print>>sys.__stdout__, Message(reply_msg)
158 if reply_msg['content']['status'] == u'error':
154 if reply_msg['content']['status'] == u'error':
159 self._abort_queue()
155 self._abort_queue()
@@ -161,22 +157,22 b' class Kernel(HasTraits):'
161 def complete_request(self, ident, parent):
157 def complete_request(self, ident, parent):
162 matches = {'matches' : self._complete(parent),
158 matches = {'matches' : self._complete(parent),
163 'status' : 'ok'}
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 matches, parent, ident)
161 matches, parent, ident)
166 print >> sys.__stdout__, completion_msg
162 print >> sys.__stdout__, completion_msg
167
163
168 def object_info_request(self, ident, parent):
164 def object_info_request(self, ident, parent):
169 context = parent['content']['oname'].split('.')
165 context = parent['content']['oname'].split('.')
170 object_info = self._object_info(context)
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 object_info, parent, ident)
168 object_info, parent, ident)
173 print >> sys.__stdout__, msg
169 print >> sys.__stdout__, msg
174
170
175 def shutdown_request(self, ident, parent):
171 def shutdown_request(self, ident, parent):
176 content = dict(parent['content'])
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 content, parent, ident)
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 content, parent, ident)
176 content, parent, ident)
181 print >> sys.__stdout__, msg
177 print >> sys.__stdout__, msg
182 time.sleep(0.1)
178 time.sleep(0.1)
@@ -197,7 +193,7 b' class Kernel(HasTraits):'
197 print>>sys.__stdout__, Message(msg)
193 print>>sys.__stdout__, Message(msg)
198 msg_type = msg['msg_type']
194 msg_type = msg['msg_type']
199 reply_type = msg_type.split('_')[0] + '_reply'
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 print>>sys.__stdout__, Message(reply_msg)
197 print>>sys.__stdout__, Message(reply_msg)
202 # We need to wait a bit for requests to come in. This can probably
198 # We need to wait a bit for requests to come in. This can probably
203 # be set shorter for true asynchronous clients.
199 # be set shorter for true asynchronous clients.
@@ -210,10 +206,10 b' class Kernel(HasTraits):'
210
206
211 # Send the input request.
207 # Send the input request.
212 content = dict(prompt=prompt)
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 # Await a response.
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 try:
213 try:
218 value = reply['content']['value']
214 value = reply['content']['value']
219 except:
215 except:
@@ -259,58 +255,26 b' class Kernel(HasTraits):'
259 # Kernel main and launch functions
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,
258 def launch_kernel(*args, **kwargs):
263 stdin=None, stdout=None, stderr=None,
259 """ Launches a simple Python kernel, binding to the specified ports.
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.
271
260
272 xrep_port : int, optional
261 This function simply calls entry_point.base_launch_kernel with the right first
273 The port to use for XREP channel.
262 command to start a pykernel. See base_launch_kernel for arguments.
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.
295
263
296 Returns
264 Returns
297 -------
265 -------
298 A tuple of form:
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 where kernel_process is a Popen object and the ports are integers.
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 return base_launch_kernel('from IPython.zmq.pykernel import main; main()',
270 return base_launch_kernel('from IPython.zmq.pykernel import main; main()',
309 xrep_port, pub_port, req_port, hb_port,
271 *args, **kwargs)
310 stdin, stdout, stderr,
311 executable, independent, extra_arguments)
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 if __name__ == '__main__':
279 if __name__ == '__main__':
316 main()
280 main()
General Comments 0
You need to be logged in to leave comments. Login now