##// END OF EJS Templates
KM.client creates a client with a shared Session
MinRK -
Show More
@@ -1,417 +1,423
1 """Base class to manage a running kernel
1 """Base class to manage a running kernel
2 """
2 """
3
3
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Copyright (C) 2013 The IPython Development Team
5 # Copyright (C) 2013 The IPython Development Team
6 #
6 #
7 # Distributed under the terms of the BSD License. The full license is in
7 # Distributed under the terms of the BSD License. The full license is in
8 # the file COPYING, distributed as part of this software.
8 # the file COPYING, distributed as part of this software.
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Imports
12 # Imports
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 from __future__ import absolute_import
15 from __future__ import absolute_import
16
16
17 # Standard library imports
17 # Standard library imports
18 import signal
18 import signal
19 import sys
19 import sys
20 import time
20 import time
21
21
22 import zmq
22 import zmq
23
23
24 # Local imports
24 # Local imports
25 from IPython.config.configurable import LoggingConfigurable
25 from IPython.config.configurable import LoggingConfigurable
26 from IPython.utils.importstring import import_item
26 from IPython.utils.importstring import import_item
27 from IPython.utils.localinterfaces import LOCAL_IPS
27 from IPython.utils.localinterfaces import LOCAL_IPS
28 from IPython.utils.traitlets import (
28 from IPython.utils.traitlets import (
29 Any, Instance, Unicode, List, Bool, Type, DottedObjectName
29 Any, Instance, Unicode, List, Bool, Type, DottedObjectName
30 )
30 )
31 from IPython.kernel import (
31 from IPython.kernel import (
32 make_ipkernel_cmd,
32 make_ipkernel_cmd,
33 launch_kernel,
33 launch_kernel,
34 )
34 )
35 from .connect import ConnectionFileMixin
35 from .connect import ConnectionFileMixin
36 from .zmq.session import Session
36 from .zmq.session import Session
37 from .managerabc import (
37 from .managerabc import (
38 KernelManagerABC
38 KernelManagerABC
39 )
39 )
40
40
41 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
42 # Main kernel manager class
42 # Main kernel manager class
43 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
44
44
45 _socket_types = {
45 _socket_types = {
46 'hb' : zmq.REQ,
46 'hb' : zmq.REQ,
47 'shell' : zmq.DEALER,
47 'shell' : zmq.DEALER,
48 'iopub' : zmq.SUB,
48 'iopub' : zmq.SUB,
49 'stdin' : zmq.DEALER,
49 'stdin' : zmq.DEALER,
50 'control': zmq.DEALER,
50 'control': zmq.DEALER,
51 }
51 }
52
52
53 class KernelManager(LoggingConfigurable, ConnectionFileMixin):
53 class KernelManager(LoggingConfigurable, ConnectionFileMixin):
54 """Manages a single kernel in a subprocess on this host.
54 """Manages a single kernel in a subprocess on this host.
55
55
56 This version starts kernels with Popen.
56 This version starts kernels with Popen.
57 """
57 """
58
58
59 # The PyZMQ Context to use for communication with the kernel.
59 # The PyZMQ Context to use for communication with the kernel.
60 context = Instance(zmq.Context)
60 context = Instance(zmq.Context)
61 def _context_default(self):
61 def _context_default(self):
62 return zmq.Context.instance()
62 return zmq.Context.instance()
63
63
64 # The Session to use for communication with the kernel.
64 # The Session to use for communication with the kernel.
65 session = Instance(Session)
65 session = Instance(Session)
66 def _session_default(self):
66 def _session_default(self):
67 return Session(config=self.config)
67 return Session(config=self.config)
68
68
69 # the class to create with our `client` method
69 # the class to create with our `client` method
70 client_class = DottedObjectName('IPython.kernel.client.KernelClient')
70 client_class = DottedObjectName('IPython.kernel.client.KernelClient')
71 client_factory = Type()
71 client_factory = Type()
72 def _client_class_changed(self, name, old, new):
72 def _client_class_changed(self, name, old, new):
73 self.client_factory = import_item(str(new))
73 self.client_factory = import_item(str(new))
74
74
75 # The kernel process with which the KernelManager is communicating.
75 # The kernel process with which the KernelManager is communicating.
76 # generally a Popen instance
76 # generally a Popen instance
77 kernel = Any()
77 kernel = Any()
78
78
79 kernel_cmd = List(Unicode, config=True,
79 kernel_cmd = List(Unicode, config=True,
80 help="""The Popen Command to launch the kernel.
80 help="""The Popen Command to launch the kernel.
81 Override this if you have a custom
81 Override this if you have a custom
82 """
82 """
83 )
83 )
84
84
85 def _kernel_cmd_changed(self, name, old, new):
85 def _kernel_cmd_changed(self, name, old, new):
86 self.ipython_kernel = False
86 self.ipython_kernel = False
87
87
88 ipython_kernel = Bool(True)
88 ipython_kernel = Bool(True)
89
89
90 # Protected traits
90 # Protected traits
91 _launch_args = Any()
91 _launch_args = Any()
92 _control_socket = Any()
92 _control_socket = Any()
93
93
94 _restarter = Any()
95
94 autorestart = Bool(False, config=True,
96 autorestart = Bool(False, config=True,
95 help="""Should we autorestart the kernel if it dies."""
97 help="""Should we autorestart the kernel if it dies."""
96 )
98 )
97
99
98 def __del__(self):
100 def __del__(self):
99 self._close_control_socket()
101 self._close_control_socket()
100 self.cleanup_connection_file()
102 self.cleanup_connection_file()
101
103
102 #--------------------------------------------------------------------------
104 #--------------------------------------------------------------------------
103 # Kernel restarter
105 # Kernel restarter
104 #--------------------------------------------------------------------------
106 #--------------------------------------------------------------------------
105
107
106 def start_restarter(self):
108 def start_restarter(self):
107 pass
109 pass
108
110
109 def stop_restarter(self):
111 def stop_restarter(self):
110 pass
112 pass
111
113
112 #--------------------------------------------------------------------------
114 #--------------------------------------------------------------------------
113 # create a Client connected to our Kernel
115 # create a Client connected to our Kernel
114 #--------------------------------------------------------------------------
116 #--------------------------------------------------------------------------
115
117
116 def client(self, **kwargs):
118 def client(self, **kwargs):
117 """Create a client configured to connect to our kernel"""
119 """Create a client configured to connect to our kernel"""
118 if self.client_factory is None:
120 if self.client_factory is None:
119 self.client_factory = import_item(self.client_class)
121 self.client_factory = import_item(self.client_class)
120
122
121 kw = {}
123 kw = {}
122 kw.update(self.get_connection_info())
124 kw.update(self.get_connection_info())
123 kw['connection_file'] = self.connection_file
125 kw.update(dict(
126 connection_file=self.connection_file,
127 session=self.session,
128 config=self.config,
129 ))
124
130
125 # add kwargs last, for manual overrides
131 # add kwargs last, for manual overrides
126 kw.update(kwargs)
132 kw.update(kwargs)
127 return self.client_factory(**kw)
133 return self.client_factory(**kw)
128
134
129 #--------------------------------------------------------------------------
135 #--------------------------------------------------------------------------
130 # Connection info
136 # Connection info
131 #--------------------------------------------------------------------------
137 #--------------------------------------------------------------------------
132
138
133 def _make_url(self, channel):
139 def _make_url(self, channel):
134 """Make a ZeroMQ URL for a given channel."""
140 """Make a ZeroMQ URL for a given channel."""
135 transport = self.transport
141 transport = self.transport
136 ip = self.ip
142 ip = self.ip
137 port = getattr(self, '%s_port' % channel)
143 port = getattr(self, '%s_port' % channel)
138
144
139 if transport == 'tcp':
145 if transport == 'tcp':
140 return "tcp://%s:%i" % (ip, port)
146 return "tcp://%s:%i" % (ip, port)
141 else:
147 else:
142 return "%s://%s-%s" % (transport, ip, port)
148 return "%s://%s-%s" % (transport, ip, port)
143
149
144 def _create_connected_socket(self, channel, identity=None):
150 def _create_connected_socket(self, channel, identity=None):
145 """Create a zmq Socket and connect it to the kernel."""
151 """Create a zmq Socket and connect it to the kernel."""
146 url = self._make_url(channel)
152 url = self._make_url(channel)
147 socket_type = _socket_types[channel]
153 socket_type = _socket_types[channel]
148 self.log.info("Connecting to: %s" % url)
154 self.log.info("Connecting to: %s" % url)
149 sock = self.context.socket(socket_type)
155 sock = self.context.socket(socket_type)
150 if identity:
156 if identity:
151 sock.identity = identity
157 sock.identity = identity
152 sock.connect(url)
158 sock.connect(url)
153 return sock
159 return sock
154
160
155 def connect_iopub(self, identity=None):
161 def connect_iopub(self, identity=None):
156 """return zmq Socket connected to the IOPub channel"""
162 """return zmq Socket connected to the IOPub channel"""
157 sock = self._create_connected_socket('iopub', identity=identity)
163 sock = self._create_connected_socket('iopub', identity=identity)
158 sock.setsockopt(zmq.SUBSCRIBE, b'')
164 sock.setsockopt(zmq.SUBSCRIBE, b'')
159 return sock
165 return sock
160
166
161 def connect_shell(self, identity=None):
167 def connect_shell(self, identity=None):
162 """return zmq Socket connected to the Shell channel"""
168 """return zmq Socket connected to the Shell channel"""
163 return self._create_connected_socket('shell', identity=identity)
169 return self._create_connected_socket('shell', identity=identity)
164
170
165 def connect_stdin(self, identity=None):
171 def connect_stdin(self, identity=None):
166 """return zmq Socket connected to the StdIn channel"""
172 """return zmq Socket connected to the StdIn channel"""
167 return self._create_connected_socket('stdin', identity=identity)
173 return self._create_connected_socket('stdin', identity=identity)
168
174
169 def connect_hb(self, identity=None):
175 def connect_hb(self, identity=None):
170 """return zmq Socket connected to the Heartbeat channel"""
176 """return zmq Socket connected to the Heartbeat channel"""
171 return self._create_connected_socket('hb', identity=identity)
177 return self._create_connected_socket('hb', identity=identity)
172
178
173 def connect_control(self, identity=None):
179 def connect_control(self, identity=None):
174 """return zmq Socket connected to the Heartbeat channel"""
180 """return zmq Socket connected to the Heartbeat channel"""
175 return self._create_connected_socket('control', identity=identity)
181 return self._create_connected_socket('control', identity=identity)
176
182
177 #--------------------------------------------------------------------------
183 #--------------------------------------------------------------------------
178 # Kernel management
184 # Kernel management
179 #--------------------------------------------------------------------------
185 #--------------------------------------------------------------------------
180
186
181 def format_kernel_cmd(self, **kw):
187 def format_kernel_cmd(self, **kw):
182 """format templated args (e.g. {connection_file})"""
188 """format templated args (e.g. {connection_file})"""
183 if self.kernel_cmd:
189 if self.kernel_cmd:
184 cmd = self.kernel_cmd
190 cmd = self.kernel_cmd
185 else:
191 else:
186 cmd = make_ipkernel_cmd(
192 cmd = make_ipkernel_cmd(
187 'from IPython.kernel.zmq.kernelapp import main; main()',
193 'from IPython.kernel.zmq.kernelapp import main; main()',
188 **kw
194 **kw
189 )
195 )
190 ns = dict(connection_file=self.connection_file)
196 ns = dict(connection_file=self.connection_file)
191 ns.update(self._launch_args)
197 ns.update(self._launch_args)
192 return [ c.format(**ns) for c in cmd ]
198 return [ c.format(**ns) for c in cmd ]
193
199
194 def _launch_kernel(self, kernel_cmd, **kw):
200 def _launch_kernel(self, kernel_cmd, **kw):
195 """actually launch the kernel
201 """actually launch the kernel
196
202
197 override in a subclass to launch kernel subprocesses differently
203 override in a subclass to launch kernel subprocesses differently
198 """
204 """
199 return launch_kernel(kernel_cmd, **kw)
205 return launch_kernel(kernel_cmd, **kw)
200
206
201 # Control socket used for polite kernel shutdown
207 # Control socket used for polite kernel shutdown
202
208
203 def _connect_control_socket(self):
209 def _connect_control_socket(self):
204 if self._control_socket is None:
210 if self._control_socket is None:
205 self._control_socket = self.connect_control()
211 self._control_socket = self.connect_control()
206 self._control_socket.linger = 100
212 self._control_socket.linger = 100
207
213
208 def _close_control_socket(self):
214 def _close_control_socket(self):
209 if self._control_socket is None:
215 if self._control_socket is None:
210 return
216 return
211 self._control_socket.close()
217 self._control_socket.close()
212 self._control_socket = None
218 self._control_socket = None
213
219
214 def start_kernel(self, **kw):
220 def start_kernel(self, **kw):
215 """Starts a kernel on this host in a separate process.
221 """Starts a kernel on this host in a separate process.
216
222
217 If random ports (port=0) are being used, this method must be called
223 If random ports (port=0) are being used, this method must be called
218 before the channels are created.
224 before the channels are created.
219
225
220 Parameters:
226 Parameters:
221 -----------
227 -----------
222 **kw : optional
228 **kw : optional
223 keyword arguments that are passed down to build the kernel_cmd
229 keyword arguments that are passed down to build the kernel_cmd
224 and launching the kernel (e.g. Popen kwargs).
230 and launching the kernel (e.g. Popen kwargs).
225 """
231 """
226 if self.transport == 'tcp' and self.ip not in LOCAL_IPS:
232 if self.transport == 'tcp' and self.ip not in LOCAL_IPS:
227 raise RuntimeError("Can only launch a kernel on a local interface. "
233 raise RuntimeError("Can only launch a kernel on a local interface. "
228 "Make sure that the '*_address' attributes are "
234 "Make sure that the '*_address' attributes are "
229 "configured properly. "
235 "configured properly. "
230 "Currently valid addresses are: %s"%LOCAL_IPS
236 "Currently valid addresses are: %s"%LOCAL_IPS
231 )
237 )
232
238
233 # write connection file / get default ports
239 # write connection file / get default ports
234 self.write_connection_file()
240 self.write_connection_file()
235
241
236 # save kwargs for use in restart
242 # save kwargs for use in restart
237 self._launch_args = kw.copy()
243 self._launch_args = kw.copy()
238 # build the Popen cmd
244 # build the Popen cmd
239 kernel_cmd = self.format_kernel_cmd(**kw)
245 kernel_cmd = self.format_kernel_cmd(**kw)
240 # launch the kernel subprocess
246 # launch the kernel subprocess
241 self.kernel = self._launch_kernel(kernel_cmd,
247 self.kernel = self._launch_kernel(kernel_cmd,
242 ipython_kernel=self.ipython_kernel,
248 ipython_kernel=self.ipython_kernel,
243 **kw)
249 **kw)
244 self.start_restarter()
250 self.start_restarter()
245 self._connect_control_socket()
251 self._connect_control_socket()
246
252
247 def _send_shutdown_request(self, restart=False):
253 def _send_shutdown_request(self, restart=False):
248 """TODO: send a shutdown request via control channel"""
254 """TODO: send a shutdown request via control channel"""
249 content = dict(restart=restart)
255 content = dict(restart=restart)
250 msg = self.session.msg("shutdown_request", content=content)
256 msg = self.session.msg("shutdown_request", content=content)
251 self.session.send(self._control_socket, msg)
257 self.session.send(self._control_socket, msg)
252
258
253 def shutdown_kernel(self, now=False, restart=False):
259 def shutdown_kernel(self, now=False, restart=False):
254 """Attempts to the stop the kernel process cleanly.
260 """Attempts to the stop the kernel process cleanly.
255
261
256 This attempts to shutdown the kernels cleanly by:
262 This attempts to shutdown the kernels cleanly by:
257
263
258 1. Sending it a shutdown message over the shell channel.
264 1. Sending it a shutdown message over the shell channel.
259 2. If that fails, the kernel is shutdown forcibly by sending it
265 2. If that fails, the kernel is shutdown forcibly by sending it
260 a signal.
266 a signal.
261
267
262 Parameters:
268 Parameters:
263 -----------
269 -----------
264 now : bool
270 now : bool
265 Should the kernel be forcible killed *now*. This skips the
271 Should the kernel be forcible killed *now*. This skips the
266 first, nice shutdown attempt.
272 first, nice shutdown attempt.
267 restart: bool
273 restart: bool
268 Will this kernel be restarted after it is shutdown. When this
274 Will this kernel be restarted after it is shutdown. When this
269 is True, connection files will not be cleaned up.
275 is True, connection files will not be cleaned up.
270 """
276 """
271 # Stop monitoring for restarting while we shutdown.
277 # Stop monitoring for restarting while we shutdown.
272 self.stop_restarter()
278 self.stop_restarter()
273
279
274 # FIXME: Shutdown does not work on Windows due to ZMQ errors!
280 # FIXME: Shutdown does not work on Windows due to ZMQ errors!
275 if sys.platform == 'win32':
281 if sys.platform == 'win32':
276 self._kill_kernel()
282 self._kill_kernel()
277 return
283 return
278
284
279 if now:
285 if now:
280 if self.has_kernel:
286 if self.has_kernel:
281 self._kill_kernel()
287 self._kill_kernel()
282 else:
288 else:
283 # Don't send any additional kernel kill messages immediately, to give
289 # Don't send any additional kernel kill messages immediately, to give
284 # the kernel a chance to properly execute shutdown actions. Wait for at
290 # the kernel a chance to properly execute shutdown actions. Wait for at
285 # most 1s, checking every 0.1s.
291 # most 1s, checking every 0.1s.
286 self._send_shutdown_request(restart=restart)
292 self._send_shutdown_request(restart=restart)
287 for i in range(10):
293 for i in range(10):
288 if self.is_alive():
294 if self.is_alive():
289 time.sleep(0.1)
295 time.sleep(0.1)
290 else:
296 else:
291 break
297 break
292 else:
298 else:
293 # OK, we've waited long enough.
299 # OK, we've waited long enough.
294 if self.has_kernel:
300 if self.has_kernel:
295 self._kill_kernel()
301 self._kill_kernel()
296
302
297 if not restart:
303 if not restart:
298 self.cleanup_connection_file()
304 self.cleanup_connection_file()
299 self.cleanup_ipc_files()
305 self.cleanup_ipc_files()
300 else:
306 else:
301 self.cleanup_ipc_files()
307 self.cleanup_ipc_files()
302
308
303 def restart_kernel(self, now=False, **kw):
309 def restart_kernel(self, now=False, **kw):
304 """Restarts a kernel with the arguments that were used to launch it.
310 """Restarts a kernel with the arguments that were used to launch it.
305
311
306 If the old kernel was launched with random ports, the same ports will be
312 If the old kernel was launched with random ports, the same ports will be
307 used for the new kernel. The same connection file is used again.
313 used for the new kernel. The same connection file is used again.
308
314
309 Parameters
315 Parameters
310 ----------
316 ----------
311 now : bool, optional
317 now : bool, optional
312 If True, the kernel is forcefully restarted *immediately*, without
318 If True, the kernel is forcefully restarted *immediately*, without
313 having a chance to do any cleanup action. Otherwise the kernel is
319 having a chance to do any cleanup action. Otherwise the kernel is
314 given 1s to clean up before a forceful restart is issued.
320 given 1s to clean up before a forceful restart is issued.
315
321
316 In all cases the kernel is restarted, the only difference is whether
322 In all cases the kernel is restarted, the only difference is whether
317 it is given a chance to perform a clean shutdown or not.
323 it is given a chance to perform a clean shutdown or not.
318
324
319 **kw : optional
325 **kw : optional
320 Any options specified here will overwrite those used to launch the
326 Any options specified here will overwrite those used to launch the
321 kernel.
327 kernel.
322 """
328 """
323 if self._launch_args is None:
329 if self._launch_args is None:
324 raise RuntimeError("Cannot restart the kernel. "
330 raise RuntimeError("Cannot restart the kernel. "
325 "No previous call to 'start_kernel'.")
331 "No previous call to 'start_kernel'.")
326 else:
332 else:
327 # Stop currently running kernel.
333 # Stop currently running kernel.
328 self.shutdown_kernel(now=now, restart=True)
334 self.shutdown_kernel(now=now, restart=True)
329
335
330 # Start new kernel.
336 # Start new kernel.
331 self._launch_args.update(kw)
337 self._launch_args.update(kw)
332 self.start_kernel(**self._launch_args)
338 self.start_kernel(**self._launch_args)
333
339
334 # FIXME: Messages get dropped in Windows due to probable ZMQ bug
340 # FIXME: Messages get dropped in Windows due to probable ZMQ bug
335 # unless there is some delay here.
341 # unless there is some delay here.
336 if sys.platform == 'win32':
342 if sys.platform == 'win32':
337 time.sleep(0.2)
343 time.sleep(0.2)
338
344
339 @property
345 @property
340 def has_kernel(self):
346 def has_kernel(self):
341 """Has a kernel been started that we are managing."""
347 """Has a kernel been started that we are managing."""
342 return self.kernel is not None
348 return self.kernel is not None
343
349
344 def _kill_kernel(self):
350 def _kill_kernel(self):
345 """Kill the running kernel.
351 """Kill the running kernel.
346
352
347 This is a private method, callers should use shutdown_kernel(now=True).
353 This is a private method, callers should use shutdown_kernel(now=True).
348 """
354 """
349 if self.has_kernel:
355 if self.has_kernel:
350
356
351 # Signal the kernel to terminate (sends SIGKILL on Unix and calls
357 # Signal the kernel to terminate (sends SIGKILL on Unix and calls
352 # TerminateProcess() on Win32).
358 # TerminateProcess() on Win32).
353 try:
359 try:
354 self.kernel.kill()
360 self.kernel.kill()
355 except OSError as e:
361 except OSError as e:
356 # In Windows, we will get an Access Denied error if the process
362 # In Windows, we will get an Access Denied error if the process
357 # has already terminated. Ignore it.
363 # has already terminated. Ignore it.
358 if sys.platform == 'win32':
364 if sys.platform == 'win32':
359 if e.winerror != 5:
365 if e.winerror != 5:
360 raise
366 raise
361 # On Unix, we may get an ESRCH error if the process has already
367 # On Unix, we may get an ESRCH error if the process has already
362 # terminated. Ignore it.
368 # terminated. Ignore it.
363 else:
369 else:
364 from errno import ESRCH
370 from errno import ESRCH
365 if e.errno != ESRCH:
371 if e.errno != ESRCH:
366 raise
372 raise
367
373
368 # Block until the kernel terminates.
374 # Block until the kernel terminates.
369 self.kernel.wait()
375 self.kernel.wait()
370 self.kernel = None
376 self.kernel = None
371 else:
377 else:
372 raise RuntimeError("Cannot kill kernel. No kernel is running!")
378 raise RuntimeError("Cannot kill kernel. No kernel is running!")
373
379
374 def interrupt_kernel(self):
380 def interrupt_kernel(self):
375 """Interrupts the kernel by sending it a signal.
381 """Interrupts the kernel by sending it a signal.
376
382
377 Unlike ``signal_kernel``, this operation is well supported on all
383 Unlike ``signal_kernel``, this operation is well supported on all
378 platforms.
384 platforms.
379 """
385 """
380 if self.has_kernel:
386 if self.has_kernel:
381 if sys.platform == 'win32':
387 if sys.platform == 'win32':
382 from .zmq.parentpoller import ParentPollerWindows as Poller
388 from .zmq.parentpoller import ParentPollerWindows as Poller
383 Poller.send_interrupt(self.kernel.win32_interrupt_event)
389 Poller.send_interrupt(self.kernel.win32_interrupt_event)
384 else:
390 else:
385 self.kernel.send_signal(signal.SIGINT)
391 self.kernel.send_signal(signal.SIGINT)
386 else:
392 else:
387 raise RuntimeError("Cannot interrupt kernel. No kernel is running!")
393 raise RuntimeError("Cannot interrupt kernel. No kernel is running!")
388
394
389 def signal_kernel(self, signum):
395 def signal_kernel(self, signum):
390 """Sends a signal to the kernel.
396 """Sends a signal to the kernel.
391
397
392 Note that since only SIGTERM is supported on Windows, this function is
398 Note that since only SIGTERM is supported on Windows, this function is
393 only useful on Unix systems.
399 only useful on Unix systems.
394 """
400 """
395 if self.has_kernel:
401 if self.has_kernel:
396 self.kernel.send_signal(signum)
402 self.kernel.send_signal(signum)
397 else:
403 else:
398 raise RuntimeError("Cannot signal kernel. No kernel is running!")
404 raise RuntimeError("Cannot signal kernel. No kernel is running!")
399
405
400 def is_alive(self):
406 def is_alive(self):
401 """Is the kernel process still running?"""
407 """Is the kernel process still running?"""
402 if self.has_kernel:
408 if self.has_kernel:
403 if self.kernel.poll() is None:
409 if self.kernel.poll() is None:
404 return True
410 return True
405 else:
411 else:
406 return False
412 return False
407 else:
413 else:
408 # we don't have a kernel
414 # we don't have a kernel
409 return False
415 return False
410
416
411
417
412 #-----------------------------------------------------------------------------
418 #-----------------------------------------------------------------------------
413 # ABC Registration
419 # ABC Registration
414 #-----------------------------------------------------------------------------
420 #-----------------------------------------------------------------------------
415
421
416 KernelManagerABC.register(KernelManager)
422 KernelManagerABC.register(KernelManager)
417
423
General Comments 0
You need to be logged in to leave comments. Login now