##// END OF EJS Templates
move multikernelmanager to IPython.kernel
MinRK -
Show More
@@ -0,0 +1,257 b''
1 """A kernel manager for multiple kernels
2
3 Authors:
4
5 * Brian Granger
6 """
7
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2013 The IPython Development Team
10 #
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
14
15 #-----------------------------------------------------------------------------
16 # Imports
17 #-----------------------------------------------------------------------------
18
19 import os
20 import uuid
21
22 import zmq
23 from zmq.eventloop.zmqstream import ZMQStream
24
25 from IPython.config.configurable import LoggingConfigurable
26 from IPython.utils.importstring import import_item
27 from IPython.utils.traitlets import (
28 Instance, Dict, Unicode, Any, DottedObjectName,
29 )
30 #-----------------------------------------------------------------------------
31 # Classes
32 #-----------------------------------------------------------------------------
33
34 class DuplicateKernelError(Exception):
35 pass
36
37
38 class MultiKernelManager(LoggingConfigurable):
39 """A class for managing multiple kernels."""
40
41 kernel_manager_class = DottedObjectName(
42 "IPython.kernel.blockingkernelmanager.BlockingKernelManager", config=True,
43 help="""The kernel manager class. This is configurable to allow
44 subclassing of the KernelManager for customized behavior.
45 """
46 )
47 def _kernel_manager_class_changed(self, name, old, new):
48 self.kernel_manager_factory = import_item(new)
49
50 kernel_manager_factory = Any(help="this is kernel_manager_class after import")
51 def _kernel_manager_factory_default(self):
52 return import_item(self.kernel_manager_class)
53
54 context = Instance('zmq.Context')
55 def _context_default(self):
56 return zmq.Context.instance()
57
58 connection_dir = Unicode('')
59
60 _kernels = Dict()
61
62 def list_kernel_ids(self):
63 """Return a list of the kernel ids of the active kernels."""
64 # Create a copy so we can iterate over kernels in operations
65 # that delete keys.
66 return list(self._kernels.keys())
67
68 def __len__(self):
69 """Return the number of running kernels."""
70 return len(self.list_kernel_ids())
71
72 def __contains__(self, kernel_id):
73 return kernel_id in self._kernels
74
75 def start_kernel(self, **kwargs):
76 """Start a new kernel.
77
78 The caller can pick a kernel_id by passing one in as a keyword arg,
79 otherwise one will be picked using a uuid.
80
81 To silence the kernel's stdout/stderr, call this using::
82
83 km.start_kernel(stdout=PIPE, stderr=PIPE)
84
85 """
86 kernel_id = kwargs.pop('kernel_id', unicode(uuid.uuid4()))
87 if kernel_id in self:
88 raise DuplicateKernelError('Kernel already exists: %s' % kernel_id)
89 # kernel_manager_factory is the constructor for the KernelManager
90 # subclass we are using. It can be configured as any Configurable,
91 # including things like its transport and ip.
92 km = self.kernel_manager_factory(connection_file=os.path.join(
93 self.connection_dir, "kernel-%s.json" % kernel_id),
94 config=self.config,
95 )
96 km.start_kernel(**kwargs)
97 # start just the shell channel, needed for graceful restart
98 km.start_channels(shell=True, iopub=False, stdin=False, hb=False)
99 self._kernels[kernel_id] = km
100 return kernel_id
101
102 def shutdown_kernel(self, kernel_id, now=False):
103 """Shutdown a kernel by its kernel uuid.
104
105 Parameters
106 ==========
107 kernel_id : uuid
108 The id of the kernel to shutdown.
109 now : bool
110 Should the kernel be shutdown forcibly using a signal.
111 """
112 k = self.get_kernel(kernel_id)
113 k.shutdown_kernel(now=now)
114 k.shell_channel.stop()
115 del self._kernels[kernel_id]
116
117 def shutdown_all(self, now=False):
118 """Shutdown all kernels."""
119 for kid in self.list_kernel_ids():
120 self.shutdown_kernel(kid, now=now)
121
122 def interrupt_kernel(self, kernel_id):
123 """Interrupt (SIGINT) the kernel by its uuid.
124
125 Parameters
126 ==========
127 kernel_id : uuid
128 The id of the kernel to interrupt.
129 """
130 return self.get_kernel(kernel_id).interrupt_kernel()
131
132 def signal_kernel(self, kernel_id, signum):
133 """Sends a signal to the kernel by its uuid.
134
135 Note that since only SIGTERM is supported on Windows, this function
136 is only useful on Unix systems.
137
138 Parameters
139 ==========
140 kernel_id : uuid
141 The id of the kernel to signal.
142 """
143 return self.get_kernel(kernel_id).signal_kernel(signum)
144
145 def restart_kernel(self, kernel_id):
146 """Restart a kernel by its uuid, keeping the same ports.
147
148 Parameters
149 ==========
150 kernel_id : uuid
151 The id of the kernel to interrupt.
152 """
153 return self.get_kernel(kernel_id).restart_kernel()
154
155 def get_kernel(self, kernel_id):
156 """Get the single KernelManager object for a kernel by its uuid.
157
158 Parameters
159 ==========
160 kernel_id : uuid
161 The id of the kernel.
162 """
163 km = self._kernels.get(kernel_id)
164 if km is not None:
165 return km
166 else:
167 raise KeyError("Kernel with id not found: %s" % kernel_id)
168
169 def get_connection_info(self, kernel_id):
170 """Return a dictionary of connection data for a kernel.
171
172 Parameters
173 ==========
174 kernel_id : uuid
175 The id of the kernel.
176
177 Returns
178 =======
179 connection_dict : dict
180 A dict of the information needed to connect to a kernel.
181 This includes the ip address and the integer port
182 numbers of the different channels (stdin_port, iopub_port,
183 shell_port, hb_port).
184 """
185 km = self.get_kernel(kernel_id)
186 return dict(transport=km.transport,
187 ip=km.ip,
188 shell_port=km.shell_port,
189 iopub_port=km.iopub_port,
190 stdin_port=km.stdin_port,
191 hb_port=km.hb_port,
192 )
193
194 def _make_url(self, transport, ip, port):
195 """Make a ZeroMQ URL for a given transport, ip and port."""
196 if transport == 'tcp':
197 return "tcp://%s:%i" % (ip, port)
198 else:
199 return "%s://%s-%s" % (transport, ip, port)
200
201 def _create_connected_stream(self, kernel_id, socket_type, channel):
202 """Create a connected ZMQStream for a kernel."""
203 cinfo = self.get_connection_info(kernel_id)
204 url = self._make_url(cinfo['transport'], cinfo['ip'],
205 cinfo['%s_port' % channel]
206 )
207 sock = self.context.socket(socket_type)
208 self.log.info("Connecting to: %s" % url)
209 sock.connect(url)
210 return ZMQStream(sock)
211
212 def create_iopub_stream(self, kernel_id):
213 """Return a ZMQStream object connected to the iopub channel.
214
215 Parameters
216 ==========
217 kernel_id : uuid
218 The id of the kernel.
219
220 Returns
221 =======
222 stream : ZMQStream
223 """
224 iopub_stream = self._create_connected_stream(kernel_id, zmq.SUB, 'iopub')
225 iopub_stream.socket.setsockopt(zmq.SUBSCRIBE, b'')
226 return iopub_stream
227
228 def create_shell_stream(self, kernel_id):
229 """Return a ZMQStream object connected to the shell channel.
230
231 Parameters
232 ==========
233 kernel_id : uuid
234 The id of the kernel.
235
236 Returns
237 =======
238 stream : ZMQStream
239 """
240 shell_stream = self._create_connected_stream(kernel_id, zmq.DEALER, 'shell')
241 return shell_stream
242
243 def create_hb_stream(self, kernel_id):
244 """Return a ZMQStream object connected to the hb channel.
245
246 Parameters
247 ==========
248 kernel_id : uuid
249 The id of the kernel.
250
251 Returns
252 =======
253 stream : ZMQStream
254 """
255 hb_stream = self._create_connected_stream(kernel_id, zmq.REQ, 'hb')
256 return hb_stream
257
@@ -1,364 +1,133 b''
1 """A kernel manager for multiple kernels.
1 """A kernel manager relating notebooks and kernels
2
2
3 Authors:
3 Authors:
4
4
5 * Brian Granger
5 * Brian Granger
6 """
6 """
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2008-2011 The IPython Development Team
9 # Copyright (C) 2013 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 import os
20 import uuid
21
22 import zmq
23 from zmq.eventloop.zmqstream import ZMQStream
24
25 from tornado import web
19 from tornado import web
26
20
27 from IPython.config.configurable import LoggingConfigurable
21 from IPython.kernel.multikernelmanager import MultiKernelManager
28 from IPython.utils.importstring import import_item
29 from IPython.utils.traitlets import (
22 from IPython.utils.traitlets import (
30 Instance, Dict, List, Unicode, Float, Integer, Any, DottedObjectName,
23 Dict, List, Unicode, Float, Integer,
31 )
24 )
32 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
33 # Classes
26 # Classes
34 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
35
28
36 class DuplicateKernelError(Exception):
37 pass
38
39
40 class MultiKernelManager(LoggingConfigurable):
41 """A class for managing multiple kernels."""
42
43 kernel_manager_class = DottedObjectName(
44 "IPython.kernel.blockingkernelmanager.BlockingKernelManager", config=True,
45 help="""The kernel manager class. This is configurable to allow
46 subclassing of the KernelManager for customized behavior.
47 """
48 )
49 def _kernel_manager_class_changed(self, name, old, new):
50 self.kernel_manager_factory = import_item(new)
51
52 kernel_manager_factory = Any(help="this is kernel_manager_class after import")
53 def _kernel_manager_factory_default(self):
54 return import_item(self.kernel_manager_class)
55
56 context = Instance('zmq.Context')
57 def _context_default(self):
58 return zmq.Context.instance()
59
60 connection_dir = Unicode('')
61
62 _kernels = Dict()
63
64 def list_kernel_ids(self):
65 """Return a list of the kernel ids of the active kernels."""
66 # Create a copy so we can iterate over kernels in operations
67 # that delete keys.
68 return list(self._kernels.keys())
69
70 def __len__(self):
71 """Return the number of running kernels."""
72 return len(self.list_kernel_ids())
73
74 def __contains__(self, kernel_id):
75 return kernel_id in self._kernels
76
77 def start_kernel(self, **kwargs):
78 """Start a new kernel.
79
80 The caller can pick a kernel_id by passing one in as a keyword arg,
81 otherwise one will be picked using a uuid.
82
83 To silence the kernel's stdout/stderr, call this using::
84
85 km.start_kernel(stdout=PIPE, stderr=PIPE)
86
87 """
88 kernel_id = kwargs.pop('kernel_id', unicode(uuid.uuid4()))
89 if kernel_id in self:
90 raise DuplicateKernelError('Kernel already exists: %s' % kernel_id)
91 # kernel_manager_factory is the constructor for the KernelManager
92 # subclass we are using. It can be configured as any Configurable,
93 # including things like its transport and ip.
94 km = self.kernel_manager_factory(connection_file=os.path.join(
95 self.connection_dir, "kernel-%s.json" % kernel_id),
96 config=self.config,
97 )
98 km.start_kernel(**kwargs)
99 # start just the shell channel, needed for graceful restart
100 km.start_channels(shell=True, iopub=False, stdin=False, hb=False)
101 self._kernels[kernel_id] = km
102 return kernel_id
103
104 def shutdown_kernel(self, kernel_id, now=False):
105 """Shutdown a kernel by its kernel uuid.
106
107 Parameters
108 ==========
109 kernel_id : uuid
110 The id of the kernel to shutdown.
111 now : bool
112 Should the kernel be shutdown forcibly using a signal.
113 """
114 k = self.get_kernel(kernel_id)
115 k.shutdown_kernel(now=now)
116 k.shell_channel.stop()
117 del self._kernels[kernel_id]
118
119 def shutdown_all(self, now=False):
120 """Shutdown all kernels."""
121 for kid in self.list_kernel_ids():
122 self.shutdown_kernel(kid, now=now)
123
124 def interrupt_kernel(self, kernel_id):
125 """Interrupt (SIGINT) the kernel by its uuid.
126
127 Parameters
128 ==========
129 kernel_id : uuid
130 The id of the kernel to interrupt.
131 """
132 return self.get_kernel(kernel_id).interrupt_kernel()
133
134 def signal_kernel(self, kernel_id, signum):
135 """Sends a signal to the kernel by its uuid.
136
137 Note that since only SIGTERM is supported on Windows, this function
138 is only useful on Unix systems.
139
140 Parameters
141 ==========
142 kernel_id : uuid
143 The id of the kernel to signal.
144 """
145 return self.get_kernel(kernel_id).signal_kernel(signum)
146
147 def restart_kernel(self, kernel_id):
148 """Restart a kernel by its uuid, keeping the same ports.
149
150 Parameters
151 ==========
152 kernel_id : uuid
153 The id of the kernel to interrupt.
154 """
155 return self.get_kernel(kernel_id).restart_kernel()
156
157 def get_kernel(self, kernel_id):
158 """Get the single KernelManager object for a kernel by its uuid.
159
160 Parameters
161 ==========
162 kernel_id : uuid
163 The id of the kernel.
164 """
165 km = self._kernels.get(kernel_id)
166 if km is not None:
167 return km
168 else:
169 raise KeyError("Kernel with id not found: %s" % kernel_id)
170
171 def get_connection_info(self, kernel_id):
172 """Return a dictionary of connection data for a kernel.
173
174 Parameters
175 ==========
176 kernel_id : uuid
177 The id of the kernel.
178
179 Returns
180 =======
181 connection_dict : dict
182 A dict of the information needed to connect to a kernel.
183 This includes the ip address and the integer port
184 numbers of the different channels (stdin_port, iopub_port,
185 shell_port, hb_port).
186 """
187 km = self.get_kernel(kernel_id)
188 return dict(transport=km.transport,
189 ip=km.ip,
190 shell_port=km.shell_port,
191 iopub_port=km.iopub_port,
192 stdin_port=km.stdin_port,
193 hb_port=km.hb_port,
194 )
195
196 def _make_url(self, transport, ip, port):
197 """Make a ZeroMQ URL for a given transport, ip and port."""
198 if transport == 'tcp':
199 return "tcp://%s:%i" % (ip, port)
200 else:
201 return "%s://%s-%s" % (transport, ip, port)
202
203 def _create_connected_stream(self, kernel_id, socket_type, channel):
204 """Create a connected ZMQStream for a kernel."""
205 cinfo = self.get_connection_info(kernel_id)
206 url = self._make_url(cinfo['transport'], cinfo['ip'],
207 cinfo['%s_port' % channel]
208 )
209 sock = self.context.socket(socket_type)
210 self.log.info("Connecting to: %s" % url)
211 sock.connect(url)
212 return ZMQStream(sock)
213
214 def create_iopub_stream(self, kernel_id):
215 """Return a ZMQStream object connected to the iopub channel.
216
217 Parameters
218 ==========
219 kernel_id : uuid
220 The id of the kernel.
221
222 Returns
223 =======
224 stream : ZMQStream
225 """
226 iopub_stream = self._create_connected_stream(kernel_id, zmq.SUB, 'iopub')
227 iopub_stream.socket.setsockopt(zmq.SUBSCRIBE, b'')
228 return iopub_stream
229
230 def create_shell_stream(self, kernel_id):
231 """Return a ZMQStream object connected to the shell channel.
232
233 Parameters
234 ==========
235 kernel_id : uuid
236 The id of the kernel.
237
238 Returns
239 =======
240 stream : ZMQStream
241 """
242 shell_stream = self._create_connected_stream(kernel_id, zmq.DEALER, 'shell')
243 return shell_stream
244
245 def create_hb_stream(self, kernel_id):
246 """Return a ZMQStream object connected to the hb channel.
247
248 Parameters
249 ==========
250 kernel_id : uuid
251 The id of the kernel.
252
253 Returns
254 =======
255 stream : ZMQStream
256 """
257 hb_stream = self._create_connected_stream(kernel_id, zmq.REQ, 'hb')
258 return hb_stream
259
260
29
261 class MappingKernelManager(MultiKernelManager):
30 class MappingKernelManager(MultiKernelManager):
262 """A KernelManager that handles notebok mapping and HTTP error handling"""
31 """A KernelManager that handles notebok mapping and HTTP error handling"""
263
32
264 kernel_argv = List(Unicode)
33 kernel_argv = List(Unicode)
265
34
266 time_to_dead = Float(3.0, config=True, help="""Kernel heartbeat interval in seconds.""")
35 time_to_dead = Float(3.0, config=True, help="""Kernel heartbeat interval in seconds.""")
267 first_beat = Float(5.0, config=True, help="Delay (in seconds) before sending first heartbeat.")
36 first_beat = Float(5.0, config=True, help="Delay (in seconds) before sending first heartbeat.")
268
37
269 max_msg_size = Integer(65536, config=True, help="""
38 max_msg_size = Integer(65536, config=True, help="""
270 The max raw message size accepted from the browser
39 The max raw message size accepted from the browser
271 over a WebSocket connection.
40 over a WebSocket connection.
272 """)
41 """)
273
42
274 _notebook_mapping = Dict()
43 _notebook_mapping = Dict()
275
44
276 #-------------------------------------------------------------------------
45 #-------------------------------------------------------------------------
277 # Methods for managing kernels and sessions
46 # Methods for managing kernels and sessions
278 #-------------------------------------------------------------------------
47 #-------------------------------------------------------------------------
279
48
280 def kernel_for_notebook(self, notebook_id):
49 def kernel_for_notebook(self, notebook_id):
281 """Return the kernel_id for a notebook_id or None."""
50 """Return the kernel_id for a notebook_id or None."""
282 return self._notebook_mapping.get(notebook_id)
51 return self._notebook_mapping.get(notebook_id)
283
52
284 def set_kernel_for_notebook(self, notebook_id, kernel_id):
53 def set_kernel_for_notebook(self, notebook_id, kernel_id):
285 """Associate a notebook with a kernel."""
54 """Associate a notebook with a kernel."""
286 if notebook_id is not None:
55 if notebook_id is not None:
287 self._notebook_mapping[notebook_id] = kernel_id
56 self._notebook_mapping[notebook_id] = kernel_id
288
57
289 def notebook_for_kernel(self, kernel_id):
58 def notebook_for_kernel(self, kernel_id):
290 """Return the notebook_id for a kernel_id or None."""
59 """Return the notebook_id for a kernel_id or None."""
291 notebook_ids = [k for k, v in self._notebook_mapping.iteritems() if v == kernel_id]
60 notebook_ids = [k for k, v in self._notebook_mapping.iteritems() if v == kernel_id]
292 if len(notebook_ids) == 1:
61 if len(notebook_ids) == 1:
293 return notebook_ids[0]
62 return notebook_ids[0]
294 else:
63 else:
295 return None
64 return None
296
65
297 def delete_mapping_for_kernel(self, kernel_id):
66 def delete_mapping_for_kernel(self, kernel_id):
298 """Remove the kernel/notebook mapping for kernel_id."""
67 """Remove the kernel/notebook mapping for kernel_id."""
299 notebook_id = self.notebook_for_kernel(kernel_id)
68 notebook_id = self.notebook_for_kernel(kernel_id)
300 if notebook_id is not None:
69 if notebook_id is not None:
301 del self._notebook_mapping[notebook_id]
70 del self._notebook_mapping[notebook_id]
302
71
303 def start_kernel(self, notebook_id=None, **kwargs):
72 def start_kernel(self, notebook_id=None, **kwargs):
304 """Start a kernel for a notebok an return its kernel_id.
73 """Start a kernel for a notebok an return its kernel_id.
305
74
306 Parameters
75 Parameters
307 ----------
76 ----------
308 notebook_id : uuid
77 notebook_id : uuid
309 The uuid of the notebook to associate the new kernel with. If this
78 The uuid of the notebook to associate the new kernel with. If this
310 is not None, this kernel will be persistent whenever the notebook
79 is not None, this kernel will be persistent whenever the notebook
311 requests a kernel.
80 requests a kernel.
312 """
81 """
313 kernel_id = self.kernel_for_notebook(notebook_id)
82 kernel_id = self.kernel_for_notebook(notebook_id)
314 if kernel_id is None:
83 if kernel_id is None:
315 kwargs['extra_arguments'] = self.kernel_argv
84 kwargs['extra_arguments'] = self.kernel_argv
316 kernel_id = super(MappingKernelManager, self).start_kernel(**kwargs)
85 kernel_id = super(MappingKernelManager, self).start_kernel(**kwargs)
317 self.set_kernel_for_notebook(notebook_id, kernel_id)
86 self.set_kernel_for_notebook(notebook_id, kernel_id)
318 self.log.info("Kernel started: %s" % kernel_id)
87 self.log.info("Kernel started: %s" % kernel_id)
319 self.log.debug("Kernel args: %r" % kwargs)
88 self.log.debug("Kernel args: %r" % kwargs)
320 else:
89 else:
321 self.log.info("Using existing kernel: %s" % kernel_id)
90 self.log.info("Using existing kernel: %s" % kernel_id)
322 return kernel_id
91 return kernel_id
323
92
324 def shutdown_kernel(self, kernel_id, now=False):
93 def shutdown_kernel(self, kernel_id, now=False):
325 """Shutdown a kernel and remove its notebook association."""
94 """Shutdown a kernel and remove its notebook association."""
326 self._check_kernel_id(kernel_id)
95 self._check_kernel_id(kernel_id)
327 super(MappingKernelManager, self).shutdown_kernel(
96 super(MappingKernelManager, self).shutdown_kernel(
328 kernel_id, now=now
97 kernel_id, now=now
329 )
98 )
330 self.delete_mapping_for_kernel(kernel_id)
99 self.delete_mapping_for_kernel(kernel_id)
331 self.log.info("Kernel shutdown: %s" % kernel_id)
100 self.log.info("Kernel shutdown: %s" % kernel_id)
332
101
333 def interrupt_kernel(self, kernel_id):
102 def interrupt_kernel(self, kernel_id):
334 """Interrupt a kernel."""
103 """Interrupt a kernel."""
335 self._check_kernel_id(kernel_id)
104 self._check_kernel_id(kernel_id)
336 super(MappingKernelManager, self).interrupt_kernel(kernel_id)
105 super(MappingKernelManager, self).interrupt_kernel(kernel_id)
337 self.log.info("Kernel interrupted: %s" % kernel_id)
106 self.log.info("Kernel interrupted: %s" % kernel_id)
338
107
339 def restart_kernel(self, kernel_id):
108 def restart_kernel(self, kernel_id):
340 """Restart a kernel while keeping clients connected."""
109 """Restart a kernel while keeping clients connected."""
341 self._check_kernel_id(kernel_id)
110 self._check_kernel_id(kernel_id)
342 super(MappingKernelManager, self).restart_kernel(kernel_id)
111 super(MappingKernelManager, self).restart_kernel(kernel_id)
343 self.log.info("Kernel restarted: %s" % kernel_id)
112 self.log.info("Kernel restarted: %s" % kernel_id)
344
113
345 def create_iopub_stream(self, kernel_id):
114 def create_iopub_stream(self, kernel_id):
346 """Create a new iopub stream."""
115 """Create a new iopub stream."""
347 self._check_kernel_id(kernel_id)
116 self._check_kernel_id(kernel_id)
348 return super(MappingKernelManager, self).create_iopub_stream(kernel_id)
117 return super(MappingKernelManager, self).create_iopub_stream(kernel_id)
349
118
350 def create_shell_stream(self, kernel_id):
119 def create_shell_stream(self, kernel_id):
351 """Create a new shell stream."""
120 """Create a new shell stream."""
352 self._check_kernel_id(kernel_id)
121 self._check_kernel_id(kernel_id)
353 return super(MappingKernelManager, self).create_shell_stream(kernel_id)
122 return super(MappingKernelManager, self).create_shell_stream(kernel_id)
354
123
355 def create_hb_stream(self, kernel_id):
124 def create_hb_stream(self, kernel_id):
356 """Create a new hb stream."""
125 """Create a new hb stream."""
357 self._check_kernel_id(kernel_id)
126 self._check_kernel_id(kernel_id)
358 return super(MappingKernelManager, self).create_hb_stream(kernel_id)
127 return super(MappingKernelManager, self).create_hb_stream(kernel_id)
359
128
360 def _check_kernel_id(self, kernel_id):
129 def _check_kernel_id(self, kernel_id):
361 """Check a that a kernel_id exists and raise 404 if not."""
130 """Check a that a kernel_id exists and raise 404 if not."""
362 if kernel_id not in self:
131 if kernel_id not in self:
363 raise web.HTTPError(404, u'Kernel does not exist: %s' % kernel_id)
132 raise web.HTTPError(404, u'Kernel does not exist: %s' % kernel_id)
364
133
@@ -1,90 +1,90 b''
1 """ Implements a fully blocking kernel manager.
1 """ Implements a fully blocking kernel manager.
2
2
3 Useful for test suites and blocking terminal interfaces.
3 Useful for test suites and blocking terminal interfaces.
4 """
4 """
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 # Copyright (C) 2010-2012 The IPython Development Team
6 # Copyright (C) 2010-2012 The IPython Development Team
7 #
7 #
8 # Distributed under the terms of the BSD License. The full license is in
8 # Distributed under the terms of the BSD License. The full license is in
9 # the file COPYING.txt, distributed as part of this software.
9 # the file COPYING.txt, distributed as part of this software.
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Imports
13 # Imports
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 import Queue
16 import Queue
17
17
18 from IPython.utils.traitlets import Type
18 from IPython.utils.traitlets import Type
19 from kernelmanager import KernelManager, IOPubChannel, HBChannel, \
19 from .kernelmanager import KernelManager, IOPubChannel, HBChannel, \
20 ShellChannel, StdInChannel
20 ShellChannel, StdInChannel
21
21
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 # Blocking kernel manager
23 # Blocking kernel manager
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26
26
27 class BlockingChannelMixin(object):
27 class BlockingChannelMixin(object):
28
28
29 def __init__(self, *args, **kwds):
29 def __init__(self, *args, **kwds):
30 super(BlockingChannelMixin, self).__init__(*args, **kwds)
30 super(BlockingChannelMixin, self).__init__(*args, **kwds)
31 self._in_queue = Queue.Queue()
31 self._in_queue = Queue.Queue()
32
32
33 def call_handlers(self, msg):
33 def call_handlers(self, msg):
34 self._in_queue.put(msg)
34 self._in_queue.put(msg)
35
35
36 def get_msg(self, block=True, timeout=None):
36 def get_msg(self, block=True, timeout=None):
37 """ Gets a message if there is one that is ready. """
37 """ Gets a message if there is one that is ready. """
38 if timeout is None:
38 if timeout is None:
39 # Queue.get(timeout=None) has stupid uninteruptible
39 # Queue.get(timeout=None) has stupid uninteruptible
40 # behavior, so wait for a week instead
40 # behavior, so wait for a week instead
41 timeout = 604800
41 timeout = 604800
42 return self._in_queue.get(block, timeout)
42 return self._in_queue.get(block, timeout)
43
43
44 def get_msgs(self):
44 def get_msgs(self):
45 """ Get all messages that are currently ready. """
45 """ Get all messages that are currently ready. """
46 msgs = []
46 msgs = []
47 while True:
47 while True:
48 try:
48 try:
49 msgs.append(self.get_msg(block=False))
49 msgs.append(self.get_msg(block=False))
50 except Queue.Empty:
50 except Queue.Empty:
51 break
51 break
52 return msgs
52 return msgs
53
53
54 def msg_ready(self):
54 def msg_ready(self):
55 """ Is there a message that has been received? """
55 """ Is there a message that has been received? """
56 return not self._in_queue.empty()
56 return not self._in_queue.empty()
57
57
58
58
59 class BlockingIOPubChannel(BlockingChannelMixin, IOPubChannel):
59 class BlockingIOPubChannel(BlockingChannelMixin, IOPubChannel):
60 pass
60 pass
61
61
62
62
63 class BlockingShellChannel(BlockingChannelMixin, ShellChannel):
63 class BlockingShellChannel(BlockingChannelMixin, ShellChannel):
64 pass
64 pass
65
65
66
66
67 class BlockingStdInChannel(BlockingChannelMixin, StdInChannel):
67 class BlockingStdInChannel(BlockingChannelMixin, StdInChannel):
68 pass
68 pass
69
69
70
70
71 class BlockingHBChannel(HBChannel):
71 class BlockingHBChannel(HBChannel):
72
72
73 # This kernel needs quicker monitoring, shorten to 1 sec.
73 # This kernel needs quicker monitoring, shorten to 1 sec.
74 # less than 0.5s is unreliable, and will get occasional
74 # less than 0.5s is unreliable, and will get occasional
75 # false reports of missed beats.
75 # false reports of missed beats.
76 time_to_dead = 1.
76 time_to_dead = 1.
77
77
78 def call_handlers(self, since_last_heartbeat):
78 def call_handlers(self, since_last_heartbeat):
79 """ Pause beating on missed heartbeat. """
79 """ Pause beating on missed heartbeat. """
80 pass
80 pass
81
81
82
82
83 class BlockingKernelManager(KernelManager):
83 class BlockingKernelManager(KernelManager):
84
84
85 # The classes to use for the various channels.
85 # The classes to use for the various channels.
86 shell_channel_class = Type(BlockingShellChannel)
86 shell_channel_class = Type(BlockingShellChannel)
87 iopub_channel_class = Type(BlockingIOPubChannel)
87 iopub_channel_class = Type(BlockingIOPubChannel)
88 stdin_channel_class = Type(BlockingStdInChannel)
88 stdin_channel_class = Type(BlockingStdInChannel)
89 hb_channel_class = Type(BlockingHBChannel)
89 hb_channel_class = Type(BlockingHBChannel)
90
90
@@ -1,80 +1,80 b''
1 """Tests for the notebook kernel and session manager."""
1 """Tests for the notebook kernel and session manager."""
2
2
3 from subprocess import PIPE
3 from subprocess import PIPE
4 import time
4 import time
5 from unittest import TestCase
5 from unittest import TestCase
6
6
7 from IPython.testing import decorators as dec
7 from IPython.testing import decorators as dec
8
8
9 from IPython.config.loader import Config
9 from IPython.config.loader import Config
10 from IPython.frontend.html.notebook.kernelmanager import MultiKernelManager
11 from IPython.utils.localinterfaces import LOCALHOST
10 from IPython.utils.localinterfaces import LOCALHOST
12 from IPython.kernel.kernelmanager import KernelManager
11 from IPython.kernel.kernelmanager import KernelManager
12 from IPython.kernel.multikernelmanager import MultiKernelManager
13
13
14 class TestKernelManager(TestCase):
14 class TestKernelManager(TestCase):
15
15
16 def _get_tcp_km(self):
16 def _get_tcp_km(self):
17 return MultiKernelManager()
17 return MultiKernelManager()
18
18
19 def _get_ipc_km(self):
19 def _get_ipc_km(self):
20 c = Config()
20 c = Config()
21 c.KernelManager.transport = 'ipc'
21 c.KernelManager.transport = 'ipc'
22 c.KernelManager.ip = 'test'
22 c.KernelManager.ip = 'test'
23 km = MultiKernelManager(config=c)
23 km = MultiKernelManager(config=c)
24 return km
24 return km
25
25
26 def _run_lifecycle(self, km):
26 def _run_lifecycle(self, km):
27 kid = km.start_kernel(stdout=PIPE, stderr=PIPE)
27 kid = km.start_kernel(stdout=PIPE, stderr=PIPE)
28 self.assertTrue(kid in km)
28 self.assertTrue(kid in km)
29 self.assertTrue(kid in km.list_kernel_ids())
29 self.assertTrue(kid in km.list_kernel_ids())
30 self.assertEqual(len(km),1)
30 self.assertEqual(len(km),1)
31 km.restart_kernel(kid)
31 km.restart_kernel(kid)
32 self.assertTrue(kid in km.list_kernel_ids())
32 self.assertTrue(kid in km.list_kernel_ids())
33 # We need a delay here to give the restarting kernel a chance to
33 # We need a delay here to give the restarting kernel a chance to
34 # restart. Otherwise, the interrupt will kill it, causing the test
34 # restart. Otherwise, the interrupt will kill it, causing the test
35 # suite to hang. The reason it *hangs* is that the shutdown
35 # suite to hang. The reason it *hangs* is that the shutdown
36 # message for the restart sometimes hasn't been sent to the kernel.
36 # message for the restart sometimes hasn't been sent to the kernel.
37 # Because linger is oo on the shell channel, the context can't
37 # Because linger is oo on the shell channel, the context can't
38 # close until the message is sent to the kernel, which is not dead.
38 # close until the message is sent to the kernel, which is not dead.
39 time.sleep(1.0)
39 time.sleep(1.0)
40 km.interrupt_kernel(kid)
40 km.interrupt_kernel(kid)
41 k = km.get_kernel(kid)
41 k = km.get_kernel(kid)
42 self.assertTrue(isinstance(k, KernelManager))
42 self.assertTrue(isinstance(k, KernelManager))
43 km.shutdown_kernel(kid)
43 km.shutdown_kernel(kid)
44 self.assertTrue(not kid in km)
44 self.assertTrue(not kid in km)
45
45
46 def _run_cinfo(self, km, transport, ip):
46 def _run_cinfo(self, km, transport, ip):
47 kid = km.start_kernel(stdout=PIPE, stderr=PIPE)
47 kid = km.start_kernel(stdout=PIPE, stderr=PIPE)
48 k = km.get_kernel(kid)
48 k = km.get_kernel(kid)
49 cinfo = km.get_connection_info(kid)
49 cinfo = km.get_connection_info(kid)
50 self.assertEqual(transport, cinfo['transport'])
50 self.assertEqual(transport, cinfo['transport'])
51 self.assertEqual(ip, cinfo['ip'])
51 self.assertEqual(ip, cinfo['ip'])
52 self.assertTrue('stdin_port' in cinfo)
52 self.assertTrue('stdin_port' in cinfo)
53 self.assertTrue('iopub_port' in cinfo)
53 self.assertTrue('iopub_port' in cinfo)
54 stream = km.create_iopub_stream(kid)
54 stream = km.create_iopub_stream(kid)
55 stream.close()
55 stream.close()
56 self.assertTrue('shell_port' in cinfo)
56 self.assertTrue('shell_port' in cinfo)
57 stream = km.create_shell_stream(kid)
57 stream = km.create_shell_stream(kid)
58 stream.close()
58 stream.close()
59 self.assertTrue('hb_port' in cinfo)
59 self.assertTrue('hb_port' in cinfo)
60 stream = km.create_hb_stream(kid)
60 stream = km.create_hb_stream(kid)
61 stream.close()
61 stream.close()
62 km.shutdown_kernel(kid)
62 km.shutdown_kernel(kid)
63
63
64 def test_tcp_lifecycle(self):
64 def test_tcp_lifecycle(self):
65 km = self._get_tcp_km()
65 km = self._get_tcp_km()
66 self._run_lifecycle(km)
66 self._run_lifecycle(km)
67
67
68 @dec.skip_win32
68 @dec.skip_win32
69 def test_tcp_cinfo(self):
69 def test_tcp_cinfo(self):
70 km = self._get_tcp_km()
70 km = self._get_tcp_km()
71 self._run_cinfo(km, 'tcp', LOCALHOST)
71 self._run_cinfo(km, 'tcp', LOCALHOST)
72
72
73 def test_ipc_lifecycle(self):
73 def test_ipc_lifecycle(self):
74 km = self._get_ipc_km()
74 km = self._get_ipc_km()
75 self._run_lifecycle(km)
75 self._run_lifecycle(km)
76
76
77 def test_ipc_cinfo(self):
77 def test_ipc_cinfo(self):
78 km = self._get_ipc_km()
78 km = self._get_ipc_km()
79 self._run_cinfo(km, 'ipc', 'test')
79 self._run_cinfo(km, 'ipc', 'test')
80
80
@@ -1,51 +1,49 b''
1 #-----------------------------------------------------------------------------
1 #-----------------------------------------------------------------------------
2 # Copyright (C) 2010 The IPython Development Team
2 # Copyright (C) 2010 The IPython Development Team
3 #
3 #
4 # Distributed under the terms of the BSD License. The full license is in
4 # Distributed under the terms of the BSD License. The full license is in
5 # the file COPYING.txt, distributed as part of this software.
5 # the file COPYING.txt, distributed as part of this software.
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Verify zmq version dependency >= 2.1.11
9 # Verify zmq version dependency >= 2.1.11
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11
11
12 import warnings
12 import warnings
13 from IPython.utils.version import check_version
13 from IPython.utils.version import check_version
14
14
15
15
16 def patch_pyzmq():
16 def patch_pyzmq():
17 """backport a few patches from newer pyzmq
17 """backport a few patches from newer pyzmq
18
18
19 These can be removed as we bump our minimum pyzmq version
19 These can be removed as we bump our minimum pyzmq version
20 """
20 """
21
21
22 import zmq
22 import zmq
23
23
24 # fallback on stdlib json if jsonlib is selected, because jsonlib breaks things.
24 # fallback on stdlib json if jsonlib is selected, because jsonlib breaks things.
25 # jsonlib support is removed from pyzmq >= 2.2.0
25 # jsonlib support is removed from pyzmq >= 2.2.0
26
26
27 from zmq.utils import jsonapi
27 from zmq.utils import jsonapi
28 if jsonapi.jsonmod.__name__ == 'jsonlib':
28 if jsonapi.jsonmod.__name__ == 'jsonlib':
29 import json
29 import json
30 jsonapi.jsonmod = json
30 jsonapi.jsonmod = json
31
31
32
32
33 def check_for_zmq(minimum_version, module='IPython.zmq'):
33 def check_for_zmq(minimum_version, module='IPython.zmq'):
34 try:
34 try:
35 import zmq
35 import zmq
36 except ImportError:
36 except ImportError:
37 raise ImportError("%s requires pyzmq >= %s"%(module, minimum_version))
37 raise ImportError("%s requires pyzmq >= %s"%(module, minimum_version))
38
38
39 pyzmq_version = zmq.__version__
39 pyzmq_version = zmq.__version__
40
40
41 if not check_version(pyzmq_version, minimum_version):
41 if not check_version(pyzmq_version, minimum_version):
42 raise ImportError("%s requires pyzmq >= %s, but you have %s"%(
42 raise ImportError("%s requires pyzmq >= %s, but you have %s"%(
43 module, minimum_version, pyzmq_version))
43 module, minimum_version, pyzmq_version))
44
44
45 check_for_zmq('2.1.11')
45 check_for_zmq('2.1.11')
46 patch_pyzmq()
46 patch_pyzmq()
47
47
48 from IPython.kernel.blockingkernelmanager import BlockingKernelManager
49 from IPython.kernel.kernelmanager import *
50 from .session import Session
48 from .session import Session
51
49
General Comments 0
You need to be logged in to leave comments. Login now