##// END OF EJS Templates
remove kernels in MKM.shutdown_all
Min RK -
Show More
@@ -1,318 +1,319 b''
1 """A kernel manager for multiple kernels"""
1 """A kernel manager for multiple kernels"""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 from __future__ import absolute_import
6 from __future__ import absolute_import
7
7
8 import os
8 import os
9 import uuid
9 import uuid
10
10
11 import zmq
11 import zmq
12
12
13 from IPython.config.configurable import LoggingConfigurable
13 from IPython.config.configurable import LoggingConfigurable
14 from IPython.utils.importstring import import_item
14 from IPython.utils.importstring import import_item
15 from IPython.utils.traitlets import (
15 from IPython.utils.traitlets import (
16 Instance, Dict, List, Unicode, Any, DottedObjectName
16 Instance, Dict, List, Unicode, Any, DottedObjectName
17 )
17 )
18 from IPython.utils.py3compat import unicode_type
18 from IPython.utils.py3compat import unicode_type
19
19
20 from .kernelspec import NATIVE_KERNEL_NAME
20 from .kernelspec import NATIVE_KERNEL_NAME
21
21
22 class DuplicateKernelError(Exception):
22 class DuplicateKernelError(Exception):
23 pass
23 pass
24
24
25
25
26 def kernel_method(f):
26 def kernel_method(f):
27 """decorator for proxying MKM.method(kernel_id) to individual KMs by ID"""
27 """decorator for proxying MKM.method(kernel_id) to individual KMs by ID"""
28 def wrapped(self, kernel_id, *args, **kwargs):
28 def wrapped(self, kernel_id, *args, **kwargs):
29 # get the kernel
29 # get the kernel
30 km = self.get_kernel(kernel_id)
30 km = self.get_kernel(kernel_id)
31 method = getattr(km, f.__name__)
31 method = getattr(km, f.__name__)
32 # call the kernel's method
32 # call the kernel's method
33 r = method(*args, **kwargs)
33 r = method(*args, **kwargs)
34 # last thing, call anything defined in the actual class method
34 # last thing, call anything defined in the actual class method
35 # such as logging messages
35 # such as logging messages
36 f(self, kernel_id, *args, **kwargs)
36 f(self, kernel_id, *args, **kwargs)
37 # return the method result
37 # return the method result
38 return r
38 return r
39 return wrapped
39 return wrapped
40
40
41
41
42 class MultiKernelManager(LoggingConfigurable):
42 class MultiKernelManager(LoggingConfigurable):
43 """A class for managing multiple kernels."""
43 """A class for managing multiple kernels."""
44
44
45 ipython_kernel_argv = List(Unicode)
45 ipython_kernel_argv = List(Unicode)
46
46
47 default_kernel_name = Unicode(NATIVE_KERNEL_NAME, config=True,
47 default_kernel_name = Unicode(NATIVE_KERNEL_NAME, config=True,
48 help="The name of the default kernel to start"
48 help="The name of the default kernel to start"
49 )
49 )
50
50
51 kernel_manager_class = DottedObjectName(
51 kernel_manager_class = DottedObjectName(
52 "IPython.kernel.ioloop.IOLoopKernelManager", config=True,
52 "IPython.kernel.ioloop.IOLoopKernelManager", config=True,
53 help="""The kernel manager class. This is configurable to allow
53 help="""The kernel manager class. This is configurable to allow
54 subclassing of the KernelManager for customized behavior.
54 subclassing of the KernelManager for customized behavior.
55 """
55 """
56 )
56 )
57 def _kernel_manager_class_changed(self, name, old, new):
57 def _kernel_manager_class_changed(self, name, old, new):
58 self.kernel_manager_factory = import_item(new)
58 self.kernel_manager_factory = import_item(new)
59
59
60 kernel_manager_factory = Any(help="this is kernel_manager_class after import")
60 kernel_manager_factory = Any(help="this is kernel_manager_class after import")
61 def _kernel_manager_factory_default(self):
61 def _kernel_manager_factory_default(self):
62 return import_item(self.kernel_manager_class)
62 return import_item(self.kernel_manager_class)
63
63
64 context = Instance('zmq.Context')
64 context = Instance('zmq.Context')
65 def _context_default(self):
65 def _context_default(self):
66 return zmq.Context.instance()
66 return zmq.Context.instance()
67
67
68 connection_dir = Unicode('')
68 connection_dir = Unicode('')
69
69
70 _kernels = Dict()
70 _kernels = Dict()
71
71
72 def list_kernel_ids(self):
72 def list_kernel_ids(self):
73 """Return a list of the kernel ids of the active kernels."""
73 """Return a list of the kernel ids of the active kernels."""
74 # Create a copy so we can iterate over kernels in operations
74 # Create a copy so we can iterate over kernels in operations
75 # that delete keys.
75 # that delete keys.
76 return list(self._kernels.keys())
76 return list(self._kernels.keys())
77
77
78 def __len__(self):
78 def __len__(self):
79 """Return the number of running kernels."""
79 """Return the number of running kernels."""
80 return len(self.list_kernel_ids())
80 return len(self.list_kernel_ids())
81
81
82 def __contains__(self, kernel_id):
82 def __contains__(self, kernel_id):
83 return kernel_id in self._kernels
83 return kernel_id in self._kernels
84
84
85 def start_kernel(self, kernel_name=None, **kwargs):
85 def start_kernel(self, kernel_name=None, **kwargs):
86 """Start a new kernel.
86 """Start a new kernel.
87
87
88 The caller can pick a kernel_id by passing one in as a keyword arg,
88 The caller can pick a kernel_id by passing one in as a keyword arg,
89 otherwise one will be picked using a uuid.
89 otherwise one will be picked using a uuid.
90
90
91 To silence the kernel's stdout/stderr, call this using::
91 To silence the kernel's stdout/stderr, call this using::
92
92
93 km.start_kernel(stdout=PIPE, stderr=PIPE)
93 km.start_kernel(stdout=PIPE, stderr=PIPE)
94
94
95 """
95 """
96 kernel_id = kwargs.pop('kernel_id', unicode_type(uuid.uuid4()))
96 kernel_id = kwargs.pop('kernel_id', unicode_type(uuid.uuid4()))
97 if kernel_id in self:
97 if kernel_id in self:
98 raise DuplicateKernelError('Kernel already exists: %s' % kernel_id)
98 raise DuplicateKernelError('Kernel already exists: %s' % kernel_id)
99
99
100 if kernel_name is None:
100 if kernel_name is None:
101 kernel_name = self.default_kernel_name
101 kernel_name = self.default_kernel_name
102 # kernel_manager_factory is the constructor for the KernelManager
102 # kernel_manager_factory is the constructor for the KernelManager
103 # subclass we are using. It can be configured as any Configurable,
103 # subclass we are using. It can be configured as any Configurable,
104 # including things like its transport and ip.
104 # including things like its transport and ip.
105 km = self.kernel_manager_factory(connection_file=os.path.join(
105 km = self.kernel_manager_factory(connection_file=os.path.join(
106 self.connection_dir, "kernel-%s.json" % kernel_id),
106 self.connection_dir, "kernel-%s.json" % kernel_id),
107 parent=self, autorestart=True, log=self.log, kernel_name=kernel_name,
107 parent=self, autorestart=True, log=self.log, kernel_name=kernel_name,
108 )
108 )
109 # FIXME: remove special treatment of IPython kernels
109 # FIXME: remove special treatment of IPython kernels
110 if km.ipython_kernel:
110 if km.ipython_kernel:
111 kwargs.setdefault('extra_arguments', self.ipython_kernel_argv)
111 kwargs.setdefault('extra_arguments', self.ipython_kernel_argv)
112 km.start_kernel(**kwargs)
112 km.start_kernel(**kwargs)
113 self._kernels[kernel_id] = km
113 self._kernels[kernel_id] = km
114 return kernel_id
114 return kernel_id
115
115
116 @kernel_method
116 @kernel_method
117 def shutdown_kernel(self, kernel_id, now=False, restart=False):
117 def shutdown_kernel(self, kernel_id, now=False, restart=False):
118 """Shutdown a kernel by its kernel uuid.
118 """Shutdown a kernel by its kernel uuid.
119
119
120 Parameters
120 Parameters
121 ==========
121 ==========
122 kernel_id : uuid
122 kernel_id : uuid
123 The id of the kernel to shutdown.
123 The id of the kernel to shutdown.
124 now : bool
124 now : bool
125 Should the kernel be shutdown forcibly using a signal.
125 Should the kernel be shutdown forcibly using a signal.
126 restart : bool
126 restart : bool
127 Will the kernel be restarted?
127 Will the kernel be restarted?
128 """
128 """
129 self.log.info("Kernel shutdown: %s" % kernel_id)
129 self.log.info("Kernel shutdown: %s" % kernel_id)
130 self.remove_kernel(kernel_id)
130 self.remove_kernel(kernel_id)
131
131
132 @kernel_method
132 @kernel_method
133 def request_shutdown(self, kernel_id, restart=False):
133 def request_shutdown(self, kernel_id, restart=False):
134 """Ask a kernel to shut down by its kernel uuid"""
134 """Ask a kernel to shut down by its kernel uuid"""
135
135
136 @kernel_method
136 @kernel_method
137 def finish_shutdown(self, kernel_id, waittime=1, pollinterval=0.1):
137 def finish_shutdown(self, kernel_id, waittime=1, pollinterval=0.1):
138 """Wait for a kernel to finish shutting down, and kill it if it doesn't
138 """Wait for a kernel to finish shutting down, and kill it if it doesn't
139 """
139 """
140 self.log.info("Kernel shutdown: %s" % kernel_id)
140 self.log.info("Kernel shutdown: %s" % kernel_id)
141
141
142 @kernel_method
142 @kernel_method
143 def cleanup(self, kernel_id, connection_file=True):
143 def cleanup(self, kernel_id, connection_file=True):
144 """Clean up a kernel's resources"""
144 """Clean up a kernel's resources"""
145
145
146 def remove_kernel(self, kernel_id):
146 def remove_kernel(self, kernel_id):
147 """remove a kernel from our mapping.
147 """remove a kernel from our mapping.
148
148
149 Mainly so that a kernel can be removed if it is already dead,
149 Mainly so that a kernel can be removed if it is already dead,
150 without having to call shutdown_kernel.
150 without having to call shutdown_kernel.
151
151
152 The kernel object is returned.
152 The kernel object is returned.
153 """
153 """
154 return self._kernels.pop(kernel_id)
154 return self._kernels.pop(kernel_id)
155
155
156 def shutdown_all(self, now=False):
156 def shutdown_all(self, now=False):
157 """Shutdown all kernels."""
157 """Shutdown all kernels."""
158 kids = self.list_kernel_ids()
158 kids = self.list_kernel_ids()
159 for kid in kids:
159 for kid in kids:
160 self.request_shutdown(kid)
160 self.request_shutdown(kid)
161 for kid in kids:
161 for kid in kids:
162 self.finish_shutdown(kid)
162 self.finish_shutdown(kid)
163 self.cleanup(kid)
163 self.cleanup(kid)
164 self.remove_kernel(kid)
164
165
165 @kernel_method
166 @kernel_method
166 def interrupt_kernel(self, kernel_id):
167 def interrupt_kernel(self, kernel_id):
167 """Interrupt (SIGINT) the kernel by its uuid.
168 """Interrupt (SIGINT) the kernel by its uuid.
168
169
169 Parameters
170 Parameters
170 ==========
171 ==========
171 kernel_id : uuid
172 kernel_id : uuid
172 The id of the kernel to interrupt.
173 The id of the kernel to interrupt.
173 """
174 """
174 self.log.info("Kernel interrupted: %s" % kernel_id)
175 self.log.info("Kernel interrupted: %s" % kernel_id)
175
176
176 @kernel_method
177 @kernel_method
177 def signal_kernel(self, kernel_id, signum):
178 def signal_kernel(self, kernel_id, signum):
178 """Sends a signal to the kernel by its uuid.
179 """Sends a signal to the kernel by its uuid.
179
180
180 Note that since only SIGTERM is supported on Windows, this function
181 Note that since only SIGTERM is supported on Windows, this function
181 is only useful on Unix systems.
182 is only useful on Unix systems.
182
183
183 Parameters
184 Parameters
184 ==========
185 ==========
185 kernel_id : uuid
186 kernel_id : uuid
186 The id of the kernel to signal.
187 The id of the kernel to signal.
187 """
188 """
188 self.log.info("Signaled Kernel %s with %s" % (kernel_id, signum))
189 self.log.info("Signaled Kernel %s with %s" % (kernel_id, signum))
189
190
190 @kernel_method
191 @kernel_method
191 def restart_kernel(self, kernel_id, now=False):
192 def restart_kernel(self, kernel_id, now=False):
192 """Restart a kernel by its uuid, keeping the same ports.
193 """Restart a kernel by its uuid, keeping the same ports.
193
194
194 Parameters
195 Parameters
195 ==========
196 ==========
196 kernel_id : uuid
197 kernel_id : uuid
197 The id of the kernel to interrupt.
198 The id of the kernel to interrupt.
198 """
199 """
199 self.log.info("Kernel restarted: %s" % kernel_id)
200 self.log.info("Kernel restarted: %s" % kernel_id)
200
201
201 @kernel_method
202 @kernel_method
202 def is_alive(self, kernel_id):
203 def is_alive(self, kernel_id):
203 """Is the kernel alive.
204 """Is the kernel alive.
204
205
205 This calls KernelManager.is_alive() which calls Popen.poll on the
206 This calls KernelManager.is_alive() which calls Popen.poll on the
206 actual kernel subprocess.
207 actual kernel subprocess.
207
208
208 Parameters
209 Parameters
209 ==========
210 ==========
210 kernel_id : uuid
211 kernel_id : uuid
211 The id of the kernel.
212 The id of the kernel.
212 """
213 """
213
214
214 def _check_kernel_id(self, kernel_id):
215 def _check_kernel_id(self, kernel_id):
215 """check that a kernel id is valid"""
216 """check that a kernel id is valid"""
216 if kernel_id not in self:
217 if kernel_id not in self:
217 raise KeyError("Kernel with id not found: %s" % kernel_id)
218 raise KeyError("Kernel with id not found: %s" % kernel_id)
218
219
219 def get_kernel(self, kernel_id):
220 def get_kernel(self, kernel_id):
220 """Get the single KernelManager object for a kernel by its uuid.
221 """Get the single KernelManager object for a kernel by its uuid.
221
222
222 Parameters
223 Parameters
223 ==========
224 ==========
224 kernel_id : uuid
225 kernel_id : uuid
225 The id of the kernel.
226 The id of the kernel.
226 """
227 """
227 self._check_kernel_id(kernel_id)
228 self._check_kernel_id(kernel_id)
228 return self._kernels[kernel_id]
229 return self._kernels[kernel_id]
229
230
230 @kernel_method
231 @kernel_method
231 def add_restart_callback(self, kernel_id, callback, event='restart'):
232 def add_restart_callback(self, kernel_id, callback, event='restart'):
232 """add a callback for the KernelRestarter"""
233 """add a callback for the KernelRestarter"""
233
234
234 @kernel_method
235 @kernel_method
235 def remove_restart_callback(self, kernel_id, callback, event='restart'):
236 def remove_restart_callback(self, kernel_id, callback, event='restart'):
236 """remove a callback for the KernelRestarter"""
237 """remove a callback for the KernelRestarter"""
237
238
238 @kernel_method
239 @kernel_method
239 def get_connection_info(self, kernel_id):
240 def get_connection_info(self, kernel_id):
240 """Return a dictionary of connection data for a kernel.
241 """Return a dictionary of connection data for a kernel.
241
242
242 Parameters
243 Parameters
243 ==========
244 ==========
244 kernel_id : uuid
245 kernel_id : uuid
245 The id of the kernel.
246 The id of the kernel.
246
247
247 Returns
248 Returns
248 =======
249 =======
249 connection_dict : dict
250 connection_dict : dict
250 A dict of the information needed to connect to a kernel.
251 A dict of the information needed to connect to a kernel.
251 This includes the ip address and the integer port
252 This includes the ip address and the integer port
252 numbers of the different channels (stdin_port, iopub_port,
253 numbers of the different channels (stdin_port, iopub_port,
253 shell_port, hb_port).
254 shell_port, hb_port).
254 """
255 """
255
256
256 @kernel_method
257 @kernel_method
257 def connect_iopub(self, kernel_id, identity=None):
258 def connect_iopub(self, kernel_id, identity=None):
258 """Return a zmq Socket connected to the iopub channel.
259 """Return a zmq Socket connected to the iopub channel.
259
260
260 Parameters
261 Parameters
261 ==========
262 ==========
262 kernel_id : uuid
263 kernel_id : uuid
263 The id of the kernel
264 The id of the kernel
264 identity : bytes (optional)
265 identity : bytes (optional)
265 The zmq identity of the socket
266 The zmq identity of the socket
266
267
267 Returns
268 Returns
268 =======
269 =======
269 stream : zmq Socket or ZMQStream
270 stream : zmq Socket or ZMQStream
270 """
271 """
271
272
272 @kernel_method
273 @kernel_method
273 def connect_shell(self, kernel_id, identity=None):
274 def connect_shell(self, kernel_id, identity=None):
274 """Return a zmq Socket connected to the shell channel.
275 """Return a zmq Socket connected to the shell channel.
275
276
276 Parameters
277 Parameters
277 ==========
278 ==========
278 kernel_id : uuid
279 kernel_id : uuid
279 The id of the kernel
280 The id of the kernel
280 identity : bytes (optional)
281 identity : bytes (optional)
281 The zmq identity of the socket
282 The zmq identity of the socket
282
283
283 Returns
284 Returns
284 =======
285 =======
285 stream : zmq Socket or ZMQStream
286 stream : zmq Socket or ZMQStream
286 """
287 """
287
288
288 @kernel_method
289 @kernel_method
289 def connect_stdin(self, kernel_id, identity=None):
290 def connect_stdin(self, kernel_id, identity=None):
290 """Return a zmq Socket connected to the stdin channel.
291 """Return a zmq Socket connected to the stdin channel.
291
292
292 Parameters
293 Parameters
293 ==========
294 ==========
294 kernel_id : uuid
295 kernel_id : uuid
295 The id of the kernel
296 The id of the kernel
296 identity : bytes (optional)
297 identity : bytes (optional)
297 The zmq identity of the socket
298 The zmq identity of the socket
298
299
299 Returns
300 Returns
300 =======
301 =======
301 stream : zmq Socket or ZMQStream
302 stream : zmq Socket or ZMQStream
302 """
303 """
303
304
304 @kernel_method
305 @kernel_method
305 def connect_hb(self, kernel_id, identity=None):
306 def connect_hb(self, kernel_id, identity=None):
306 """Return a zmq Socket connected to the hb channel.
307 """Return a zmq Socket connected to the hb channel.
307
308
308 Parameters
309 Parameters
309 ==========
310 ==========
310 kernel_id : uuid
311 kernel_id : uuid
311 The id of the kernel
312 The id of the kernel
312 identity : bytes (optional)
313 identity : bytes (optional)
313 The zmq identity of the socket
314 The zmq identity of the socket
314
315
315 Returns
316 Returns
316 =======
317 =======
317 stream : zmq Socket or ZMQStream
318 stream : zmq Socket or ZMQStream
318 """
319 """
General Comments 0
You need to be logged in to leave comments. Login now