##// END OF EJS Templates
Remove obsolete Windows workarounds, per @minrk's testing
Thomas Kluyver -
Show More
@@ -1,428 +1,420 b''
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!
256 if sys.platform == 'win32' and self.has_kernel:
257 return self._kill_kernel()
258 content = dict(restart=restart)
255 content = dict(restart=restart)
259 msg = self.session.msg("shutdown_request", content=content)
256 msg = self.session.msg("shutdown_request", content=content)
260 self.session.send(self._control_socket, msg)
257 self.session.send(self._control_socket, msg)
261
258
262 def finish_shutdown(self, waittime=1, pollinterval=0.1):
259 def finish_shutdown(self, waittime=1, pollinterval=0.1):
263 """Wait for kernel shutdown, then kill process if it doesn't shutdown.
260 """Wait for kernel shutdown, then kill process if it doesn't shutdown.
264
261
265 This does not send shutdown requests - use :meth:`request_shutdown`
262 This does not send shutdown requests - use :meth:`request_shutdown`
266 first.
263 first.
267 """
264 """
268 for i in range(int(waittime/pollinterval)):
265 for i in range(int(waittime/pollinterval)):
269 if self.is_alive():
266 if self.is_alive():
270 time.sleep(pollinterval)
267 time.sleep(pollinterval)
271 else:
268 else:
272 break
269 break
273 else:
270 else:
274 # OK, we've waited long enough.
271 # OK, we've waited long enough.
275 if self.has_kernel:
272 if self.has_kernel:
276 self._kill_kernel()
273 self._kill_kernel()
277
274
278 def cleanup(self, connection_file=True):
275 def cleanup(self, connection_file=True):
279 """Clean up resources when the kernel is shut down"""
276 """Clean up resources when the kernel is shut down"""
280 if connection_file:
277 if connection_file:
281 self.cleanup_connection_file()
278 self.cleanup_connection_file()
282
279
283 self.cleanup_ipc_files()
280 self.cleanup_ipc_files()
284 self._close_control_socket()
281 self._close_control_socket()
285
282
286 def shutdown_kernel(self, now=False, restart=False):
283 def shutdown_kernel(self, now=False, restart=False):
287 """Attempts to the stop the kernel process cleanly.
284 """Attempts to the stop the kernel process cleanly.
288
285
289 This attempts to shutdown the kernels cleanly by:
286 This attempts to shutdown the kernels cleanly by:
290
287
291 1. Sending it a shutdown message over the shell channel.
288 1. Sending it a shutdown message over the shell channel.
292 2. If that fails, the kernel is shutdown forcibly by sending it
289 2. If that fails, the kernel is shutdown forcibly by sending it
293 a signal.
290 a signal.
294
291
295 Parameters
292 Parameters
296 ----------
293 ----------
297 now : bool
294 now : bool
298 Should the kernel be forcible killed *now*. This skips the
295 Should the kernel be forcible killed *now*. This skips the
299 first, nice shutdown attempt.
296 first, nice shutdown attempt.
300 restart: bool
297 restart: bool
301 Will this kernel be restarted after it is shutdown. When this
298 Will this kernel be restarted after it is shutdown. When this
302 is True, connection files will not be cleaned up.
299 is True, connection files will not be cleaned up.
303 """
300 """
304 # Stop monitoring for restarting while we shutdown.
301 # Stop monitoring for restarting while we shutdown.
305 self.stop_restarter()
302 self.stop_restarter()
306
303
307 if now:
304 if now:
308 self._kill_kernel()
305 self._kill_kernel()
309 else:
306 else:
310 self.request_shutdown(restart=restart)
307 self.request_shutdown(restart=restart)
311 # Don't send any additional kernel kill messages immediately, to give
308 # Don't send any additional kernel kill messages immediately, to give
312 # the kernel a chance to properly execute shutdown actions. Wait for at
309 # the kernel a chance to properly execute shutdown actions. Wait for at
313 # most 1s, checking every 0.1s.
310 # most 1s, checking every 0.1s.
314 self.finish_shutdown()
311 self.finish_shutdown()
315
312
316 self.cleanup(connection_file=not restart)
313 self.cleanup(connection_file=not restart)
317
314
318 def restart_kernel(self, now=False, **kw):
315 def restart_kernel(self, now=False, **kw):
319 """Restarts a kernel with the arguments that were used to launch it.
316 """Restarts a kernel with the arguments that were used to launch it.
320
317
321 If the old kernel was launched with random ports, the same ports will be
318 If the old kernel was launched with random ports, the same ports will be
322 used for the new kernel. The same connection file is used again.
319 used for the new kernel. The same connection file is used again.
323
320
324 Parameters
321 Parameters
325 ----------
322 ----------
326 now : bool, optional
323 now : bool, optional
327 If True, the kernel is forcefully restarted *immediately*, without
324 If True, the kernel is forcefully restarted *immediately*, without
328 having a chance to do any cleanup action. Otherwise the kernel is
325 having a chance to do any cleanup action. Otherwise the kernel is
329 given 1s to clean up before a forceful restart is issued.
326 given 1s to clean up before a forceful restart is issued.
330
327
331 In all cases the kernel is restarted, the only difference is whether
328 In all cases the kernel is restarted, the only difference is whether
332 it is given a chance to perform a clean shutdown or not.
329 it is given a chance to perform a clean shutdown or not.
333
330
334 **kw : optional
331 **kw : optional
335 Any options specified here will overwrite those used to launch the
332 Any options specified here will overwrite those used to launch the
336 kernel.
333 kernel.
337 """
334 """
338 if self._launch_args is None:
335 if self._launch_args is None:
339 raise RuntimeError("Cannot restart the kernel. "
336 raise RuntimeError("Cannot restart the kernel. "
340 "No previous call to 'start_kernel'.")
337 "No previous call to 'start_kernel'.")
341 else:
338 else:
342 # Stop currently running kernel.
339 # Stop currently running kernel.
343 self.shutdown_kernel(now=now, restart=True)
340 self.shutdown_kernel(now=now, restart=True)
344
341
345 # Start new kernel.
342 # Start new kernel.
346 self._launch_args.update(kw)
343 self._launch_args.update(kw)
347 self.start_kernel(**self._launch_args)
344 self.start_kernel(**self._launch_args)
348
345
349 # FIXME: Messages get dropped in Windows due to probable ZMQ bug
350 # unless there is some delay here.
351 if sys.platform == 'win32':
352 time.sleep(0.2)
353
354 @property
346 @property
355 def has_kernel(self):
347 def has_kernel(self):
356 """Has a kernel been started that we are managing."""
348 """Has a kernel been started that we are managing."""
357 return self.kernel is not None
349 return self.kernel is not None
358
350
359 def _kill_kernel(self):
351 def _kill_kernel(self):
360 """Kill the running kernel.
352 """Kill the running kernel.
361
353
362 This is a private method, callers should use shutdown_kernel(now=True).
354 This is a private method, callers should use shutdown_kernel(now=True).
363 """
355 """
364 if self.has_kernel:
356 if self.has_kernel:
365
357
366 # Signal the kernel to terminate (sends SIGKILL on Unix and calls
358 # Signal the kernel to terminate (sends SIGKILL on Unix and calls
367 # TerminateProcess() on Win32).
359 # TerminateProcess() on Win32).
368 try:
360 try:
369 self.kernel.kill()
361 self.kernel.kill()
370 except OSError as e:
362 except OSError as e:
371 # In Windows, we will get an Access Denied error if the process
363 # In Windows, we will get an Access Denied error if the process
372 # has already terminated. Ignore it.
364 # has already terminated. Ignore it.
373 if sys.platform == 'win32':
365 if sys.platform == 'win32':
374 if e.winerror != 5:
366 if e.winerror != 5:
375 raise
367 raise
376 # On Unix, we may get an ESRCH error if the process has already
368 # On Unix, we may get an ESRCH error if the process has already
377 # terminated. Ignore it.
369 # terminated. Ignore it.
378 else:
370 else:
379 from errno import ESRCH
371 from errno import ESRCH
380 if e.errno != ESRCH:
372 if e.errno != ESRCH:
381 raise
373 raise
382
374
383 # Block until the kernel terminates.
375 # Block until the kernel terminates.
384 self.kernel.wait()
376 self.kernel.wait()
385 self.kernel = None
377 self.kernel = None
386 else:
378 else:
387 raise RuntimeError("Cannot kill kernel. No kernel is running!")
379 raise RuntimeError("Cannot kill kernel. No kernel is running!")
388
380
389 def interrupt_kernel(self):
381 def interrupt_kernel(self):
390 """Interrupts the kernel by sending it a signal.
382 """Interrupts the kernel by sending it a signal.
391
383
392 Unlike ``signal_kernel``, this operation is well supported on all
384 Unlike ``signal_kernel``, this operation is well supported on all
393 platforms.
385 platforms.
394 """
386 """
395 if self.has_kernel:
387 if self.has_kernel:
396 if sys.platform == 'win32':
388 if sys.platform == 'win32':
397 from .zmq.parentpoller import ParentPollerWindows as Poller
389 from .zmq.parentpoller import ParentPollerWindows as Poller
398 Poller.send_interrupt(self.kernel.win32_interrupt_event)
390 Poller.send_interrupt(self.kernel.win32_interrupt_event)
399 else:
391 else:
400 self.kernel.send_signal(signal.SIGINT)
392 self.kernel.send_signal(signal.SIGINT)
401 else:
393 else:
402 raise RuntimeError("Cannot interrupt kernel. No kernel is running!")
394 raise RuntimeError("Cannot interrupt kernel. No kernel is running!")
403
395
404 def signal_kernel(self, signum):
396 def signal_kernel(self, signum):
405 """Sends a signal to the kernel.
397 """Sends a signal to the kernel.
406
398
407 Note that since only SIGTERM is supported on Windows, this function is
399 Note that since only SIGTERM is supported on Windows, this function is
408 only useful on Unix systems.
400 only useful on Unix systems.
409 """
401 """
410 if self.has_kernel:
402 if self.has_kernel:
411 self.kernel.send_signal(signum)
403 self.kernel.send_signal(signum)
412 else:
404 else:
413 raise RuntimeError("Cannot signal kernel. No kernel is running!")
405 raise RuntimeError("Cannot signal kernel. No kernel is running!")
414
406
415 def is_alive(self):
407 def is_alive(self):
416 """Is the kernel process still running?"""
408 """Is the kernel process still running?"""
417 if self.has_kernel:
409 if self.has_kernel:
418 if self.kernel.poll() is None:
410 if self.kernel.poll() is None:
419 return True
411 return True
420 else:
412 else:
421 return False
413 return False
422 else:
414 else:
423 # we don't have a kernel
415 # we don't have a kernel
424 return False
416 return False
425
417
426
418
427 KernelManagerABC.register(KernelManager)
419 KernelManagerABC.register(KernelManager)
428
420
General Comments 0
You need to be logged in to leave comments. Login now