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