##// END OF EJS Templates
s/make_kernel_cmd/make_ipkernel_cmd/
MinRK -
Show More
@@ -1,262 +1,262 b''
1 1 """ Defines helper functions for creating kernel entry points and process
2 2 launchers.
3 3 """
4 4
5 5 # Standard library imports
6 6 import json
7 7 import os
8 8 import socket
9 9 from subprocess import Popen, PIPE
10 10 import sys
11 11 import tempfile
12 12
13 13 # System library imports
14 14
15 15 # IPython imports
16 16 from IPython.utils.localinterfaces import LOCALHOST
17 17 from IPython.utils.py3compat import bytes_to_str
18 18
19 19 # Local imports
20 20 from parentpoller import ParentPollerWindows
21 21
22 22 def write_connection_file(fname=None, shell_port=0, iopub_port=0, stdin_port=0, hb_port=0,
23 23 ip=LOCALHOST, key=b'', transport='tcp'):
24 24 """Generates a JSON config file, including the selection of random ports.
25 25
26 26 Parameters
27 27 ----------
28 28
29 29 fname : unicode
30 30 The path to the file to write
31 31
32 32 shell_port : int, optional
33 33 The port to use for ROUTER channel.
34 34
35 35 iopub_port : int, optional
36 36 The port to use for the SUB channel.
37 37
38 38 stdin_port : int, optional
39 39 The port to use for the REQ (raw input) channel.
40 40
41 41 hb_port : int, optional
42 42 The port to use for the hearbeat REP channel.
43 43
44 44 ip : str, optional
45 45 The ip address the kernel will bind to.
46 46
47 47 key : str, optional
48 48 The Session key used for HMAC authentication.
49 49
50 50 """
51 51 # default to temporary connector file
52 52 if not fname:
53 53 fname = tempfile.mktemp('.json')
54 54
55 55 # Find open ports as necessary.
56 56
57 57 ports = []
58 58 ports_needed = int(shell_port <= 0) + int(iopub_port <= 0) + \
59 59 int(stdin_port <= 0) + int(hb_port <= 0)
60 60 if transport == 'tcp':
61 61 for i in range(ports_needed):
62 62 sock = socket.socket()
63 63 sock.bind(('', 0))
64 64 ports.append(sock)
65 65 for i, sock in enumerate(ports):
66 66 port = sock.getsockname()[1]
67 67 sock.close()
68 68 ports[i] = port
69 69 else:
70 70 N = 1
71 71 for i in range(ports_needed):
72 72 while os.path.exists("%s-%s" % (ip, str(N))):
73 73 N += 1
74 74 ports.append(N)
75 75 N += 1
76 76 if shell_port <= 0:
77 77 shell_port = ports.pop(0)
78 78 if iopub_port <= 0:
79 79 iopub_port = ports.pop(0)
80 80 if stdin_port <= 0:
81 81 stdin_port = ports.pop(0)
82 82 if hb_port <= 0:
83 83 hb_port = ports.pop(0)
84 84
85 85 cfg = dict( shell_port=shell_port,
86 86 iopub_port=iopub_port,
87 87 stdin_port=stdin_port,
88 88 hb_port=hb_port,
89 89 )
90 90 cfg['ip'] = ip
91 91 cfg['key'] = bytes_to_str(key)
92 92 cfg['transport'] = transport
93 93
94 94 with open(fname, 'w') as f:
95 95 f.write(json.dumps(cfg, indent=2))
96 96
97 97 return fname, cfg
98 98
99 99
100 def make_kernel_cmd(code, executable=None, extra_arguments=[], **kw):
101 """ Launches a localhost kernel, binding to the specified ports.
100 def make_ipkernel_cmd(code, executable=None, extra_arguments=[], **kw):
101 """Build Popen command list for launching an IPython kernel.
102 102
103 103 Parameters
104 104 ----------
105 105 code : str,
106 106 A string of Python code that imports and executes a kernel entry point.
107 107
108 108 executable : str, optional (default sys.executable)
109 109 The Python executable to use for the kernel process.
110 110
111 111 extra_arguments : list, optional
112 112 A list of extra arguments to pass when executing the launch code.
113 113
114 114 Returns
115 115 -------
116 116
117 117 A Popen command list
118 118 """
119 119
120 120 # Build the kernel launch command.
121 121 if executable is None:
122 122 executable = sys.executable
123 123 arguments = [ executable, '-c', code, '-f', '{connection_file}' ]
124 124 arguments.extend(extra_arguments)
125 125
126 126 # Spawn a kernel.
127 127 if sys.platform == 'win32':
128 128
129 129 # If the kernel is running on pythonw and stdout/stderr are not been
130 130 # re-directed, it will crash when more than 4KB of data is written to
131 131 # stdout or stderr. This is a bug that has been with Python for a very
132 132 # long time; see http://bugs.python.org/issue706263.
133 133 # A cleaner solution to this problem would be to pass os.devnull to
134 134 # Popen directly. Unfortunately, that does not work.
135 135 if executable.endswith('pythonw.exe'):
136 136 arguments.append('--no-stdout')
137 137 arguments.append('--no-stderr')
138 138
139 139 return arguments
140 140
141 141
142 142 def launch_kernel(cmd, stdin=None, stdout=None, stderr=None,
143 143 independent=False,
144 144 cwd=None, ipython_kernel=True,
145 145 **kw
146 146 ):
147 147 """ Launches a localhost kernel, binding to the specified ports.
148 148
149 149 Parameters
150 150 ----------
151 151 cmd : Popen list,
152 152 A string of Python code that imports and executes a kernel entry point.
153 153
154 154 stdin, stdout, stderr : optional (default None)
155 155 Standards streams, as defined in subprocess.Popen.
156 156
157 157 independent : bool, optional (default False)
158 158 If set, the kernel process is guaranteed to survive if this process
159 159 dies. If not set, an effort is made to ensure that the kernel is killed
160 160 when this process dies. Note that in this case it is still good practice
161 161 to kill kernels manually before exiting.
162 162
163 163 cwd : path, optional
164 164 The working dir of the kernel process (default: cwd of this process).
165 165
166 166 ipython_kernel : bool, optional
167 167 Whether the kernel is an official IPython one,
168 168 and should get a bit of special treatment.
169 169
170 170 Returns
171 171 -------
172 172
173 173 Popen instance for the kernel subprocess
174 174 """
175 175
176 176 # Popen will fail (sometimes with a deadlock) if stdin, stdout, and stderr
177 177 # are invalid. Unfortunately, there is in general no way to detect whether
178 178 # they are valid. The following two blocks redirect them to (temporary)
179 179 # pipes in certain important cases.
180 180
181 181 # If this process has been backgrounded, our stdin is invalid. Since there
182 182 # is no compelling reason for the kernel to inherit our stdin anyway, we'll
183 183 # place this one safe and always redirect.
184 184 redirect_in = True
185 185 _stdin = PIPE if stdin is None else stdin
186 186
187 187 # If this process in running on pythonw, we know that stdin, stdout, and
188 188 # stderr are all invalid.
189 189 redirect_out = sys.executable.endswith('pythonw.exe')
190 190 if redirect_out:
191 191 _stdout = PIPE if stdout is None else stdout
192 192 _stderr = PIPE if stderr is None else stderr
193 193 else:
194 194 _stdout, _stderr = stdout, stderr
195 195
196 196 # Spawn a kernel.
197 197 if sys.platform == 'win32':
198 198
199 199 # Create a Win32 event for interrupting the kernel.
200 200 interrupt_event = ParentPollerWindows.create_interrupt_event()
201 201 if ipython_kernel:
202 202 cmd += [ '--interrupt=%i' % interrupt_event ]
203 203
204 204 # If the kernel is running on pythonw and stdout/stderr are not been
205 205 # re-directed, it will crash when more than 4KB of data is written to
206 206 # stdout or stderr. This is a bug that has been with Python for a very
207 207 # long time; see http://bugs.python.org/issue706263.
208 208 # A cleaner solution to this problem would be to pass os.devnull to
209 209 # Popen directly. Unfortunately, that does not work.
210 210 if cmd[0].endswith('pythonw.exe'):
211 211 if stdout is None:
212 212 cmd.append('--no-stdout')
213 213 if stderr is None:
214 214 cmd.append('--no-stderr')
215 215
216 216 # Launch the kernel process.
217 217 if independent:
218 218 proc = Popen(cmd,
219 219 creationflags=512, # CREATE_NEW_PROCESS_GROUP
220 220 stdin=_stdin, stdout=_stdout, stderr=_stderr)
221 221 else:
222 222 if ipython_kernel:
223 223 try:
224 224 from _winapi import DuplicateHandle, GetCurrentProcess, \
225 225 DUPLICATE_SAME_ACCESS
226 226 except:
227 227 from _subprocess import DuplicateHandle, GetCurrentProcess, \
228 228 DUPLICATE_SAME_ACCESS
229 229 pid = GetCurrentProcess()
230 230 handle = DuplicateHandle(pid, pid, pid, 0,
231 231 True, # Inheritable by new processes.
232 232 DUPLICATE_SAME_ACCESS)
233 233 cmd +=[ '--parent=%i' % handle ]
234 234
235 235
236 236 proc = Popen(cmd,
237 237 stdin=_stdin, stdout=_stdout, stderr=_stderr, cwd=cwd)
238 238
239 239 # Attach the interrupt event to the Popen objet so it can be used later.
240 240 proc.win32_interrupt_event = interrupt_event
241 241
242 242 else:
243 243 if independent:
244 244 proc = Popen(cmd, preexec_fn=lambda: os.setsid(),
245 245 stdin=_stdin, stdout=_stdout, stderr=_stderr, cwd=cwd)
246 246 else:
247 247 if ipython_kernel:
248 248 cmd += ['--parent=1']
249 249 proc = Popen(cmd,
250 250 stdin=_stdin, stdout=_stdout, stderr=_stderr, cwd=cwd)
251 251
252 252 # Clean up pipes created to work around Popen bug.
253 253 if redirect_in:
254 254 if stdin is None:
255 255 proc.stdin.close()
256 256 if redirect_out:
257 257 if stdout is None:
258 258 proc.stdout.close()
259 259 if stderr is None:
260 260 proc.stderr.close()
261 261
262 262 return proc
@@ -1,1134 +1,1134 b''
1 1 """Base classes to manage the interaction with a running kernel.
2 2
3 3 TODO
4 4 * Create logger to handle debugging and console messages.
5 5 """
6 6
7 7 #-----------------------------------------------------------------------------
8 8 # Copyright (C) 2008-2011 The IPython Development Team
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 12 #-----------------------------------------------------------------------------
13 13
14 14 #-----------------------------------------------------------------------------
15 15 # Imports
16 16 #-----------------------------------------------------------------------------
17 17
18 18 # Standard library imports.
19 19 import atexit
20 20 import errno
21 21 import json
22 22 from subprocess import Popen
23 23 import os
24 24 import signal
25 25 import sys
26 26 from threading import Thread
27 27 import time
28 28
29 29 # System library imports.
30 30 import zmq
31 31 # import ZMQError in top-level namespace, to avoid ugly attribute-error messages
32 32 # during garbage collection of threads at exit:
33 33 from zmq import ZMQError
34 34 from zmq.eventloop import ioloop, zmqstream
35 35
36 36 # Local imports.
37 37 from IPython.config.configurable import Configurable
38 38 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
39 39 from IPython.utils.traitlets import (
40 40 Any, Instance, Type, Unicode, List, Integer, Bool, CaselessStrEnum
41 41 )
42 42 from IPython.utils.py3compat import str_to_bytes
43 43 from IPython.zmq.entry_point import (
44 44 write_connection_file,
45 make_kernel_cmd,
45 make_ipkernel_cmd,
46 46 launch_kernel,
47 47 )
48 48 from session import Session
49 49 from IPython.zmq.kernelmanagerabc import (
50 50 ShellChannelABC, IOPubChannelABC,
51 51 HBChannelABC, StdInChannelABC,
52 52 KernelManagerABC
53 53 )
54 54
55 55
56 56 #-----------------------------------------------------------------------------
57 57 # Constants and exceptions
58 58 #-----------------------------------------------------------------------------
59 59
60 60 class InvalidPortNumber(Exception):
61 61 pass
62 62
63 63 #-----------------------------------------------------------------------------
64 64 # Utility functions
65 65 #-----------------------------------------------------------------------------
66 66
67 67 # some utilities to validate message structure, these might get moved elsewhere
68 68 # if they prove to have more generic utility
69 69
70 70 def validate_string_list(lst):
71 71 """Validate that the input is a list of strings.
72 72
73 73 Raises ValueError if not."""
74 74 if not isinstance(lst, list):
75 75 raise ValueError('input %r must be a list' % lst)
76 76 for x in lst:
77 77 if not isinstance(x, basestring):
78 78 raise ValueError('element %r in list must be a string' % x)
79 79
80 80
81 81 def validate_string_dict(dct):
82 82 """Validate that the input is a dict with string keys and values.
83 83
84 84 Raises ValueError if not."""
85 85 for k,v in dct.iteritems():
86 86 if not isinstance(k, basestring):
87 87 raise ValueError('key %r in dict must be a string' % k)
88 88 if not isinstance(v, basestring):
89 89 raise ValueError('value %r in dict must be a string' % v)
90 90
91 91
92 92 #-----------------------------------------------------------------------------
93 93 # ZMQ Socket Channel classes
94 94 #-----------------------------------------------------------------------------
95 95
96 96 class ZMQSocketChannel(Thread):
97 97 """The base class for the channels that use ZMQ sockets."""
98 98 context = None
99 99 session = None
100 100 socket = None
101 101 ioloop = None
102 102 stream = None
103 103 _address = None
104 104 _exiting = False
105 105
106 106 def __init__(self, context, session, address):
107 107 """Create a channel.
108 108
109 109 Parameters
110 110 ----------
111 111 context : :class:`zmq.Context`
112 112 The ZMQ context to use.
113 113 session : :class:`session.Session`
114 114 The session to use.
115 115 address : zmq url
116 116 Standard (ip, port) tuple that the kernel is listening on.
117 117 """
118 118 super(ZMQSocketChannel, self).__init__()
119 119 self.daemon = True
120 120
121 121 self.context = context
122 122 self.session = session
123 123 if isinstance(address, tuple):
124 124 if address[1] == 0:
125 125 message = 'The port number for a channel cannot be 0.'
126 126 raise InvalidPortNumber(message)
127 127 address = "tcp://%s:%i" % address
128 128 self._address = address
129 129 atexit.register(self._notice_exit)
130 130
131 131 def _notice_exit(self):
132 132 self._exiting = True
133 133
134 134 def _run_loop(self):
135 135 """Run my loop, ignoring EINTR events in the poller"""
136 136 while True:
137 137 try:
138 138 self.ioloop.start()
139 139 except ZMQError as e:
140 140 if e.errno == errno.EINTR:
141 141 continue
142 142 else:
143 143 raise
144 144 except Exception:
145 145 if self._exiting:
146 146 break
147 147 else:
148 148 raise
149 149 else:
150 150 break
151 151
152 152 def stop(self):
153 153 """Stop the channel's event loop and join its thread.
154 154
155 155 This calls :method:`Thread.join` and returns when the thread
156 156 terminates. :class:`RuntimeError` will be raised if
157 157 :method:`self.start` is called again.
158 158 """
159 159 self.join()
160 160
161 161 @property
162 162 def address(self):
163 163 """Get the channel's address as a zmq url string.
164 164
165 165 These URLS have the form: 'tcp://127.0.0.1:5555'.
166 166 """
167 167 return self._address
168 168
169 169 def _queue_send(self, msg):
170 170 """Queue a message to be sent from the IOLoop's thread.
171 171
172 172 Parameters
173 173 ----------
174 174 msg : message to send
175 175
176 176 This is threadsafe, as it uses IOLoop.add_callback to give the loop's
177 177 thread control of the action.
178 178 """
179 179 def thread_send():
180 180 self.session.send(self.stream, msg)
181 181 self.ioloop.add_callback(thread_send)
182 182
183 183 def _handle_recv(self, msg):
184 184 """Callback for stream.on_recv.
185 185
186 186 Unpacks message, and calls handlers with it.
187 187 """
188 188 ident,smsg = self.session.feed_identities(msg)
189 189 self.call_handlers(self.session.unserialize(smsg))
190 190
191 191
192 192
193 193 class ShellChannel(ZMQSocketChannel):
194 194 """The shell channel for issuing request/replies to the kernel."""
195 195
196 196 command_queue = None
197 197 # flag for whether execute requests should be allowed to call raw_input:
198 198 allow_stdin = True
199 199
200 200 def __init__(self, context, session, address):
201 201 super(ShellChannel, self).__init__(context, session, address)
202 202 self.ioloop = ioloop.IOLoop()
203 203
204 204 def run(self):
205 205 """The thread's main activity. Call start() instead."""
206 206 self.socket = self.context.socket(zmq.DEALER)
207 207 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
208 208 self.socket.connect(self.address)
209 209 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
210 210 self.stream.on_recv(self._handle_recv)
211 211 self._run_loop()
212 212 try:
213 213 self.socket.close()
214 214 except:
215 215 pass
216 216
217 217 def stop(self):
218 218 """Stop the channel's event loop and join its thread."""
219 219 self.ioloop.stop()
220 220 super(ShellChannel, self).stop()
221 221
222 222 def call_handlers(self, msg):
223 223 """This method is called in the ioloop thread when a message arrives.
224 224
225 225 Subclasses should override this method to handle incoming messages.
226 226 It is important to remember that this method is called in the thread
227 227 so that some logic must be done to ensure that the application leve
228 228 handlers are called in the application thread.
229 229 """
230 230 raise NotImplementedError('call_handlers must be defined in a subclass.')
231 231
232 232 def execute(self, code, silent=False, store_history=True,
233 233 user_variables=None, user_expressions=None, allow_stdin=None):
234 234 """Execute code in the kernel.
235 235
236 236 Parameters
237 237 ----------
238 238 code : str
239 239 A string of Python code.
240 240
241 241 silent : bool, optional (default False)
242 242 If set, the kernel will execute the code as quietly possible, and
243 243 will force store_history to be False.
244 244
245 245 store_history : bool, optional (default True)
246 246 If set, the kernel will store command history. This is forced
247 247 to be False if silent is True.
248 248
249 249 user_variables : list, optional
250 250 A list of variable names to pull from the user's namespace. They
251 251 will come back as a dict with these names as keys and their
252 252 :func:`repr` as values.
253 253
254 254 user_expressions : dict, optional
255 255 A dict mapping names to expressions to be evaluated in the user's
256 256 dict. The expression values are returned as strings formatted using
257 257 :func:`repr`.
258 258
259 259 allow_stdin : bool, optional (default self.allow_stdin)
260 260 Flag for whether the kernel can send stdin requests to frontends.
261 261
262 262 Some frontends (e.g. the Notebook) do not support stdin requests.
263 263 If raw_input is called from code executed from such a frontend, a
264 264 StdinNotImplementedError will be raised.
265 265
266 266 Returns
267 267 -------
268 268 The msg_id of the message sent.
269 269 """
270 270 if user_variables is None:
271 271 user_variables = []
272 272 if user_expressions is None:
273 273 user_expressions = {}
274 274 if allow_stdin is None:
275 275 allow_stdin = self.allow_stdin
276 276
277 277
278 278 # Don't waste network traffic if inputs are invalid
279 279 if not isinstance(code, basestring):
280 280 raise ValueError('code %r must be a string' % code)
281 281 validate_string_list(user_variables)
282 282 validate_string_dict(user_expressions)
283 283
284 284 # Create class for content/msg creation. Related to, but possibly
285 285 # not in Session.
286 286 content = dict(code=code, silent=silent, store_history=store_history,
287 287 user_variables=user_variables,
288 288 user_expressions=user_expressions,
289 289 allow_stdin=allow_stdin,
290 290 )
291 291 msg = self.session.msg('execute_request', content)
292 292 self._queue_send(msg)
293 293 return msg['header']['msg_id']
294 294
295 295 def complete(self, text, line, cursor_pos, block=None):
296 296 """Tab complete text in the kernel's namespace.
297 297
298 298 Parameters
299 299 ----------
300 300 text : str
301 301 The text to complete.
302 302 line : str
303 303 The full line of text that is the surrounding context for the
304 304 text to complete.
305 305 cursor_pos : int
306 306 The position of the cursor in the line where the completion was
307 307 requested.
308 308 block : str, optional
309 309 The full block of code in which the completion is being requested.
310 310
311 311 Returns
312 312 -------
313 313 The msg_id of the message sent.
314 314 """
315 315 content = dict(text=text, line=line, block=block, cursor_pos=cursor_pos)
316 316 msg = self.session.msg('complete_request', content)
317 317 self._queue_send(msg)
318 318 return msg['header']['msg_id']
319 319
320 320 def object_info(self, oname, detail_level=0):
321 321 """Get metadata information about an object in the kernel's namespace.
322 322
323 323 Parameters
324 324 ----------
325 325 oname : str
326 326 A string specifying the object name.
327 327 detail_level : int, optional
328 328 The level of detail for the introspection (0-2)
329 329
330 330 Returns
331 331 -------
332 332 The msg_id of the message sent.
333 333 """
334 334 content = dict(oname=oname, detail_level=detail_level)
335 335 msg = self.session.msg('object_info_request', content)
336 336 self._queue_send(msg)
337 337 return msg['header']['msg_id']
338 338
339 339 def history(self, raw=True, output=False, hist_access_type='range', **kwargs):
340 340 """Get entries from the kernel's history list.
341 341
342 342 Parameters
343 343 ----------
344 344 raw : bool
345 345 If True, return the raw input.
346 346 output : bool
347 347 If True, then return the output as well.
348 348 hist_access_type : str
349 349 'range' (fill in session, start and stop params), 'tail' (fill in n)
350 350 or 'search' (fill in pattern param).
351 351
352 352 session : int
353 353 For a range request, the session from which to get lines. Session
354 354 numbers are positive integers; negative ones count back from the
355 355 current session.
356 356 start : int
357 357 The first line number of a history range.
358 358 stop : int
359 359 The final (excluded) line number of a history range.
360 360
361 361 n : int
362 362 The number of lines of history to get for a tail request.
363 363
364 364 pattern : str
365 365 The glob-syntax pattern for a search request.
366 366
367 367 Returns
368 368 -------
369 369 The msg_id of the message sent.
370 370 """
371 371 content = dict(raw=raw, output=output, hist_access_type=hist_access_type,
372 372 **kwargs)
373 373 msg = self.session.msg('history_request', content)
374 374 self._queue_send(msg)
375 375 return msg['header']['msg_id']
376 376
377 377 def kernel_info(self):
378 378 """Request kernel info."""
379 379 msg = self.session.msg('kernel_info_request')
380 380 self._queue_send(msg)
381 381 return msg['header']['msg_id']
382 382
383 383 def shutdown(self, restart=False):
384 384 """Request an immediate kernel shutdown.
385 385
386 386 Upon receipt of the (empty) reply, client code can safely assume that
387 387 the kernel has shut down and it's safe to forcefully terminate it if
388 388 it's still alive.
389 389
390 390 The kernel will send the reply via a function registered with Python's
391 391 atexit module, ensuring it's truly done as the kernel is done with all
392 392 normal operation.
393 393 """
394 394 # Send quit message to kernel. Once we implement kernel-side setattr,
395 395 # this should probably be done that way, but for now this will do.
396 396 msg = self.session.msg('shutdown_request', {'restart':restart})
397 397 self._queue_send(msg)
398 398 return msg['header']['msg_id']
399 399
400 400
401 401
402 402 class IOPubChannel(ZMQSocketChannel):
403 403 """The iopub channel which listens for messages that the kernel publishes.
404 404
405 405 This channel is where all output is published to frontends.
406 406 """
407 407
408 408 def __init__(self, context, session, address):
409 409 super(IOPubChannel, self).__init__(context, session, address)
410 410 self.ioloop = ioloop.IOLoop()
411 411
412 412 def run(self):
413 413 """The thread's main activity. Call start() instead."""
414 414 self.socket = self.context.socket(zmq.SUB)
415 415 self.socket.setsockopt(zmq.SUBSCRIBE,b'')
416 416 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
417 417 self.socket.connect(self.address)
418 418 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
419 419 self.stream.on_recv(self._handle_recv)
420 420 self._run_loop()
421 421 try:
422 422 self.socket.close()
423 423 except:
424 424 pass
425 425
426 426 def stop(self):
427 427 """Stop the channel's event loop and join its thread."""
428 428 self.ioloop.stop()
429 429 super(IOPubChannel, self).stop()
430 430
431 431 def call_handlers(self, msg):
432 432 """This method is called in the ioloop thread when a message arrives.
433 433
434 434 Subclasses should override this method to handle incoming messages.
435 435 It is important to remember that this method is called in the thread
436 436 so that some logic must be done to ensure that the application leve
437 437 handlers are called in the application thread.
438 438 """
439 439 raise NotImplementedError('call_handlers must be defined in a subclass.')
440 440
441 441 def flush(self, timeout=1.0):
442 442 """Immediately processes all pending messages on the iopub channel.
443 443
444 444 Callers should use this method to ensure that :method:`call_handlers`
445 445 has been called for all messages that have been received on the
446 446 0MQ SUB socket of this channel.
447 447
448 448 This method is thread safe.
449 449
450 450 Parameters
451 451 ----------
452 452 timeout : float, optional
453 453 The maximum amount of time to spend flushing, in seconds. The
454 454 default is one second.
455 455 """
456 456 # We do the IOLoop callback process twice to ensure that the IOLoop
457 457 # gets to perform at least one full poll.
458 458 stop_time = time.time() + timeout
459 459 for i in xrange(2):
460 460 self._flushed = False
461 461 self.ioloop.add_callback(self._flush)
462 462 while not self._flushed and time.time() < stop_time:
463 463 time.sleep(0.01)
464 464
465 465 def _flush(self):
466 466 """Callback for :method:`self.flush`."""
467 467 self.stream.flush()
468 468 self._flushed = True
469 469
470 470
471 471 class StdInChannel(ZMQSocketChannel):
472 472 """The stdin channel to handle raw_input requests that the kernel makes."""
473 473
474 474 msg_queue = None
475 475
476 476 def __init__(self, context, session, address):
477 477 super(StdInChannel, self).__init__(context, session, address)
478 478 self.ioloop = ioloop.IOLoop()
479 479
480 480 def run(self):
481 481 """The thread's main activity. Call start() instead."""
482 482 self.socket = self.context.socket(zmq.DEALER)
483 483 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
484 484 self.socket.connect(self.address)
485 485 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
486 486 self.stream.on_recv(self._handle_recv)
487 487 self._run_loop()
488 488 try:
489 489 self.socket.close()
490 490 except:
491 491 pass
492 492
493 493 def stop(self):
494 494 """Stop the channel's event loop and join its thread."""
495 495 self.ioloop.stop()
496 496 super(StdInChannel, self).stop()
497 497
498 498 def call_handlers(self, msg):
499 499 """This method is called in the ioloop thread when a message arrives.
500 500
501 501 Subclasses should override this method to handle incoming messages.
502 502 It is important to remember that this method is called in the thread
503 503 so that some logic must be done to ensure that the application leve
504 504 handlers are called in the application thread.
505 505 """
506 506 raise NotImplementedError('call_handlers must be defined in a subclass.')
507 507
508 508 def input(self, string):
509 509 """Send a string of raw input to the kernel."""
510 510 content = dict(value=string)
511 511 msg = self.session.msg('input_reply', content)
512 512 self._queue_send(msg)
513 513
514 514
515 515 class HBChannel(ZMQSocketChannel):
516 516 """The heartbeat channel which monitors the kernel heartbeat.
517 517
518 518 Note that the heartbeat channel is paused by default. As long as you start
519 519 this channel, the kernel manager will ensure that it is paused and un-paused
520 520 as appropriate.
521 521 """
522 522
523 523 time_to_dead = 3.0
524 524 socket = None
525 525 poller = None
526 526 _running = None
527 527 _pause = None
528 528 _beating = None
529 529
530 530 def __init__(self, context, session, address):
531 531 super(HBChannel, self).__init__(context, session, address)
532 532 self._running = False
533 533 self._pause =True
534 534 self.poller = zmq.Poller()
535 535
536 536 def _create_socket(self):
537 537 if self.socket is not None:
538 538 # close previous socket, before opening a new one
539 539 self.poller.unregister(self.socket)
540 540 self.socket.close()
541 541 self.socket = self.context.socket(zmq.REQ)
542 542 self.socket.setsockopt(zmq.LINGER, 0)
543 543 self.socket.connect(self.address)
544 544
545 545 self.poller.register(self.socket, zmq.POLLIN)
546 546
547 547 def _poll(self, start_time):
548 548 """poll for heartbeat replies until we reach self.time_to_dead.
549 549
550 550 Ignores interrupts, and returns the result of poll(), which
551 551 will be an empty list if no messages arrived before the timeout,
552 552 or the event tuple if there is a message to receive.
553 553 """
554 554
555 555 until_dead = self.time_to_dead - (time.time() - start_time)
556 556 # ensure poll at least once
557 557 until_dead = max(until_dead, 1e-3)
558 558 events = []
559 559 while True:
560 560 try:
561 561 events = self.poller.poll(1000 * until_dead)
562 562 except ZMQError as e:
563 563 if e.errno == errno.EINTR:
564 564 # ignore interrupts during heartbeat
565 565 # this may never actually happen
566 566 until_dead = self.time_to_dead - (time.time() - start_time)
567 567 until_dead = max(until_dead, 1e-3)
568 568 pass
569 569 else:
570 570 raise
571 571 except Exception:
572 572 if self._exiting:
573 573 break
574 574 else:
575 575 raise
576 576 else:
577 577 break
578 578 return events
579 579
580 580 def run(self):
581 581 """The thread's main activity. Call start() instead."""
582 582 self._create_socket()
583 583 self._running = True
584 584 self._beating = True
585 585
586 586 while self._running:
587 587 if self._pause:
588 588 # just sleep, and skip the rest of the loop
589 589 time.sleep(self.time_to_dead)
590 590 continue
591 591
592 592 since_last_heartbeat = 0.0
593 593 # io.rprint('Ping from HB channel') # dbg
594 594 # no need to catch EFSM here, because the previous event was
595 595 # either a recv or connect, which cannot be followed by EFSM
596 596 self.socket.send(b'ping')
597 597 request_time = time.time()
598 598 ready = self._poll(request_time)
599 599 if ready:
600 600 self._beating = True
601 601 # the poll above guarantees we have something to recv
602 602 self.socket.recv()
603 603 # sleep the remainder of the cycle
604 604 remainder = self.time_to_dead - (time.time() - request_time)
605 605 if remainder > 0:
606 606 time.sleep(remainder)
607 607 continue
608 608 else:
609 609 # nothing was received within the time limit, signal heart failure
610 610 self._beating = False
611 611 since_last_heartbeat = time.time() - request_time
612 612 self.call_handlers(since_last_heartbeat)
613 613 # and close/reopen the socket, because the REQ/REP cycle has been broken
614 614 self._create_socket()
615 615 continue
616 616 try:
617 617 self.socket.close()
618 618 except:
619 619 pass
620 620
621 621 def pause(self):
622 622 """Pause the heartbeat."""
623 623 self._pause = True
624 624
625 625 def unpause(self):
626 626 """Unpause the heartbeat."""
627 627 self._pause = False
628 628
629 629 def is_beating(self):
630 630 """Is the heartbeat running and responsive (and not paused)."""
631 631 if self.is_alive() and not self._pause and self._beating:
632 632 return True
633 633 else:
634 634 return False
635 635
636 636 def stop(self):
637 637 """Stop the channel's event loop and join its thread."""
638 638 self._running = False
639 639 super(HBChannel, self).stop()
640 640
641 641 def call_handlers(self, since_last_heartbeat):
642 642 """This method is called in the ioloop thread when a message arrives.
643 643
644 644 Subclasses should override this method to handle incoming messages.
645 645 It is important to remember that this method is called in the thread
646 646 so that some logic must be done to ensure that the application level
647 647 handlers are called in the application thread.
648 648 """
649 649 raise NotImplementedError('call_handlers must be defined in a subclass.')
650 650
651 651
652 652 #-----------------------------------------------------------------------------
653 653 # Main kernel manager class
654 654 #-----------------------------------------------------------------------------
655 655
656 656 class KernelManager(Configurable):
657 657 """Manages a single kernel on this host along with its channels.
658 658
659 659 There are four channels associated with each kernel:
660 660
661 661 * shell: for request/reply calls to the kernel.
662 662 * iopub: for the kernel to publish results to frontends.
663 663 * hb: for monitoring the kernel's heartbeat.
664 664 * stdin: for frontends to reply to raw_input calls in the kernel.
665 665
666 666 The usage of the channels that this class manages is optional. It is
667 667 entirely possible to connect to the kernels directly using ZeroMQ
668 668 sockets. These channels are useful primarily for talking to a kernel
669 669 whose :class:`KernelManager` is in the same process.
670 670
671 671 This version manages kernels started using Popen.
672 672 """
673 673 # The PyZMQ Context to use for communication with the kernel.
674 674 context = Instance(zmq.Context)
675 675 def _context_default(self):
676 676 return zmq.Context.instance()
677 677
678 678 # The Session to use for communication with the kernel.
679 679 session = Instance(Session)
680 680 def _session_default(self):
681 681 return Session(config=self.config)
682 682
683 683 # The kernel process with which the KernelManager is communicating.
684 684 # generally a Popen instance
685 685 kernel = Any()
686 686
687 687 kernel_cmd = List(Unicode, config=True,
688 688 help="""The Popen Command to launch the kernel.
689 689 Override this if you have a custom
690 690 """
691 691 )
692 692 def _kernel_cmd_changed(self, name, old, new):
693 693 print 'kernel cmd changed', new
694 694 self.ipython_kernel = False
695 695
696 696 ipython_kernel = Bool(True)
697 697
698 698
699 699 # The addresses for the communication channels.
700 700 connection_file = Unicode('')
701 701
702 702 transport = CaselessStrEnum(['tcp', 'ipc'], default_value='tcp', config=True)
703 703
704 704 ip = Unicode(LOCALHOST, config=True,
705 705 help="""Set the kernel\'s IP address [default localhost].
706 706 If the IP address is something other than localhost, then
707 707 Consoles on other machines will be able to connect
708 708 to the Kernel, so be careful!"""
709 709 )
710 710 def _ip_default(self):
711 711 if self.transport == 'ipc':
712 712 if self.connection_file:
713 713 return os.path.splitext(self.connection_file)[0] + '-ipc'
714 714 else:
715 715 return 'kernel-ipc'
716 716 else:
717 717 return LOCALHOST
718 718 def _ip_changed(self, name, old, new):
719 719 if new == '*':
720 720 self.ip = '0.0.0.0'
721 721 shell_port = Integer(0)
722 722 iopub_port = Integer(0)
723 723 stdin_port = Integer(0)
724 724 hb_port = Integer(0)
725 725
726 726 # The classes to use for the various channels.
727 727 shell_channel_class = Type(ShellChannel)
728 728 iopub_channel_class = Type(IOPubChannel)
729 729 stdin_channel_class = Type(StdInChannel)
730 730 hb_channel_class = Type(HBChannel)
731 731
732 732 # Protected traits.
733 733 _launch_args = Any
734 734 _shell_channel = Any
735 735 _iopub_channel = Any
736 736 _stdin_channel = Any
737 737 _hb_channel = Any
738 738 _connection_file_written=Bool(False)
739 739
740 740 def __del__(self):
741 741 self.cleanup_connection_file()
742 742
743 743 #--------------------------------------------------------------------------
744 744 # Channel management methods:
745 745 #--------------------------------------------------------------------------
746 746
747 747 def start_channels(self, shell=True, iopub=True, stdin=True, hb=True):
748 748 """Starts the channels for this kernel.
749 749
750 750 This will create the channels if they do not exist and then start
751 751 them (their activity runs in a thread). If port numbers of 0 are
752 752 being used (random ports) then you must first call
753 753 :method:`start_kernel`. If the channels have been stopped and you
754 754 call this, :class:`RuntimeError` will be raised.
755 755 """
756 756 if shell:
757 757 self.shell_channel.start()
758 758 if iopub:
759 759 self.iopub_channel.start()
760 760 if stdin:
761 761 self.stdin_channel.start()
762 762 self.shell_channel.allow_stdin = True
763 763 else:
764 764 self.shell_channel.allow_stdin = False
765 765 if hb:
766 766 self.hb_channel.start()
767 767
768 768 def stop_channels(self):
769 769 """Stops all the running channels for this kernel.
770 770
771 771 This stops their event loops and joins their threads.
772 772 """
773 773 if self.shell_channel.is_alive():
774 774 self.shell_channel.stop()
775 775 if self.iopub_channel.is_alive():
776 776 self.iopub_channel.stop()
777 777 if self.stdin_channel.is_alive():
778 778 self.stdin_channel.stop()
779 779 if self.hb_channel.is_alive():
780 780 self.hb_channel.stop()
781 781
782 782 @property
783 783 def channels_running(self):
784 784 """Are any of the channels created and running?"""
785 785 return (self.shell_channel.is_alive() or self.iopub_channel.is_alive() or
786 786 self.stdin_channel.is_alive() or self.hb_channel.is_alive())
787 787
788 788 def _make_url(self, port):
789 789 """Make a zmq url with a port.
790 790
791 791 There are two cases that this handles:
792 792
793 793 * tcp: tcp://ip:port
794 794 * ipc: ipc://ip-port
795 795 """
796 796 if self.transport == 'tcp':
797 797 return "tcp://%s:%i" % (self.ip, port)
798 798 else:
799 799 return "%s://%s-%s" % (self.transport, self.ip, port)
800 800
801 801 @property
802 802 def shell_channel(self):
803 803 """Get the shell channel object for this kernel."""
804 804 if self._shell_channel is None:
805 805 self._shell_channel = self.shell_channel_class(
806 806 self.context, self.session, self._make_url(self.shell_port)
807 807 )
808 808 return self._shell_channel
809 809
810 810 @property
811 811 def iopub_channel(self):
812 812 """Get the iopub channel object for this kernel."""
813 813 if self._iopub_channel is None:
814 814 self._iopub_channel = self.iopub_channel_class(
815 815 self.context, self.session, self._make_url(self.iopub_port)
816 816 )
817 817 return self._iopub_channel
818 818
819 819 @property
820 820 def stdin_channel(self):
821 821 """Get the stdin channel object for this kernel."""
822 822 if self._stdin_channel is None:
823 823 self._stdin_channel = self.stdin_channel_class(
824 824 self.context, self.session, self._make_url(self.stdin_port)
825 825 )
826 826 return self._stdin_channel
827 827
828 828 @property
829 829 def hb_channel(self):
830 830 """Get the hb channel object for this kernel."""
831 831 if self._hb_channel is None:
832 832 self._hb_channel = self.hb_channel_class(
833 833 self.context, self.session, self._make_url(self.hb_port)
834 834 )
835 835 return self._hb_channel
836 836
837 837 #--------------------------------------------------------------------------
838 838 # Connection and ipc file management
839 839 #--------------------------------------------------------------------------
840 840
841 841 def cleanup_connection_file(self):
842 842 """Cleanup connection file *if we wrote it*
843 843
844 844 Will not raise if the connection file was already removed somehow.
845 845 """
846 846 if self._connection_file_written:
847 847 # cleanup connection files on full shutdown of kernel we started
848 848 self._connection_file_written = False
849 849 try:
850 850 os.remove(self.connection_file)
851 851 except (IOError, OSError):
852 852 pass
853 853
854 854 def cleanup_ipc_files(self):
855 855 """Cleanup ipc files if we wrote them."""
856 856 if self.transport != 'ipc':
857 857 return
858 858 for port in (self.shell_port, self.iopub_port, self.stdin_port, self.hb_port):
859 859 ipcfile = "%s-%i" % (self.ip, port)
860 860 try:
861 861 os.remove(ipcfile)
862 862 except (IOError, OSError):
863 863 pass
864 864
865 865 def load_connection_file(self):
866 866 """Load connection info from JSON dict in self.connection_file."""
867 867 with open(self.connection_file) as f:
868 868 cfg = json.loads(f.read())
869 869
870 870 from pprint import pprint
871 871 pprint(cfg)
872 872 self.transport = cfg.get('transport', 'tcp')
873 873 self.ip = cfg['ip']
874 874 self.shell_port = cfg['shell_port']
875 875 self.stdin_port = cfg['stdin_port']
876 876 self.iopub_port = cfg['iopub_port']
877 877 self.hb_port = cfg['hb_port']
878 878 self.session.key = str_to_bytes(cfg['key'])
879 879
880 880 def write_connection_file(self):
881 881 """Write connection info to JSON dict in self.connection_file."""
882 882 if self._connection_file_written:
883 883 return
884 884 self.connection_file,cfg = write_connection_file(self.connection_file,
885 885 transport=self.transport, ip=self.ip, key=self.session.key,
886 886 stdin_port=self.stdin_port, iopub_port=self.iopub_port,
887 887 shell_port=self.shell_port, hb_port=self.hb_port)
888 888 # write_connection_file also sets default ports:
889 889 self.shell_port = cfg['shell_port']
890 890 self.stdin_port = cfg['stdin_port']
891 891 self.iopub_port = cfg['iopub_port']
892 892 self.hb_port = cfg['hb_port']
893 893
894 894 self._connection_file_written = True
895 895
896 896 #--------------------------------------------------------------------------
897 897 # Kernel management
898 898 #--------------------------------------------------------------------------
899 899
900 900 def format_kernel_cmd(self, **kw):
901 901 """format templated args (e.g. {connection_file})"""
902 902 if self.kernel_cmd:
903 903 cmd = self.kernel_cmd
904 904 else:
905 cmd = make_kernel_cmd(
905 cmd = make_ipkernel_cmd(
906 906 'from IPython.zmq.ipkernel import main; main()',
907 907 **kw
908 908 )
909 909 ns = dict(connection_file=self.connection_file)
910 910 ns.update(self._launch_args)
911 911 return [ c.format(**ns) for c in cmd ]
912 912
913 913 def _launch_kernel(self, kernel_cmd, **kw):
914 914 """actually launch the kernel
915 915
916 916 override in a subclass to launch kernel subprocesses differently
917 917 """
918 918 return launch_kernel(kernel_cmd, **kw)
919 919
920 920 def start_kernel(self, **kw):
921 921 """Starts a kernel on this host in a separate process.
922 922
923 923 If random ports (port=0) are being used, this method must be called
924 924 before the channels are created.
925 925
926 926 Parameters:
927 927 -----------
928 928 launcher : callable, optional (default None)
929 929 A custom function for launching the kernel process (generally a
930 930 wrapper around ``entry_point.base_launch_kernel``). In most cases,
931 931 it should not be necessary to use this parameter.
932 932
933 933 **kw : optional
934 934 keyword arguments that are passed down into the launcher
935 935 callable.
936 936 """
937 937 if self.transport == 'tcp' and self.ip not in LOCAL_IPS:
938 938 raise RuntimeError("Can only launch a kernel on a local interface. "
939 939 "Make sure that the '*_address' attributes are "
940 940 "configured properly. "
941 941 "Currently valid addresses are: %s"%LOCAL_IPS
942 942 )
943 943
944 944 # write connection file / get default ports
945 945 self.write_connection_file()
946 946
947 947 # save kwargs for use in restart
948 948 self._launch_args = kw.copy()
949 949 # build the Popen cmd
950 950 kernel_cmd = self.format_kernel_cmd(**kw)
951 951 # launch the kernel subprocess
952 952 self.kernel = self._launch_kernel(kernel_cmd,
953 953 ipython_kernel=self.ipython_kernel,
954 954 **kw)
955 955
956 956 def shutdown_kernel(self, now=False, restart=False):
957 957 """Attempts to the stop the kernel process cleanly.
958 958
959 959 This attempts to shutdown the kernels cleanly by:
960 960
961 961 1. Sending it a shutdown message over the shell channel.
962 962 2. If that fails, the kernel is shutdown forcibly by sending it
963 963 a signal.
964 964
965 965 Parameters:
966 966 -----------
967 967 now : bool
968 968 Should the kernel be forcible killed *now*. This skips the
969 969 first, nice shutdown attempt.
970 970 restart: bool
971 971 Will this kernel be restarted after it is shutdown. When this
972 972 is True, connection files will not be cleaned up.
973 973 """
974 974 # FIXME: Shutdown does not work on Windows due to ZMQ errors!
975 975 if sys.platform == 'win32':
976 976 self._kill_kernel()
977 977 return
978 978
979 979 # Pause the heart beat channel if it exists.
980 980 if self._hb_channel is not None:
981 981 self._hb_channel.pause()
982 982
983 983 if now:
984 984 if self.has_kernel:
985 985 self._kill_kernel()
986 986 else:
987 987 # Don't send any additional kernel kill messages immediately, to give
988 988 # the kernel a chance to properly execute shutdown actions. Wait for at
989 989 # most 1s, checking every 0.1s.
990 990 self.shell_channel.shutdown(restart=restart)
991 991 for i in range(10):
992 992 if self.is_alive:
993 993 time.sleep(0.1)
994 994 else:
995 995 break
996 996 else:
997 997 # OK, we've waited long enough.
998 998 if self.has_kernel:
999 999 self._kill_kernel()
1000 1000
1001 1001 if not restart:
1002 1002 self.cleanup_connection_file()
1003 1003 self.cleanup_ipc_files()
1004 1004 else:
1005 1005 self.cleanup_ipc_files()
1006 1006
1007 1007 def restart_kernel(self, now=False, **kw):
1008 1008 """Restarts a kernel with the arguments that were used to launch it.
1009 1009
1010 1010 If the old kernel was launched with random ports, the same ports will be
1011 1011 used for the new kernel. The same connection file is used again.
1012 1012
1013 1013 Parameters
1014 1014 ----------
1015 1015 now : bool, optional
1016 1016 If True, the kernel is forcefully restarted *immediately*, without
1017 1017 having a chance to do any cleanup action. Otherwise the kernel is
1018 1018 given 1s to clean up before a forceful restart is issued.
1019 1019
1020 1020 In all cases the kernel is restarted, the only difference is whether
1021 1021 it is given a chance to perform a clean shutdown or not.
1022 1022
1023 1023 **kw : optional
1024 1024 Any options specified here will overwrite those used to launch the
1025 1025 kernel.
1026 1026 """
1027 1027 if self._launch_args is None:
1028 1028 raise RuntimeError("Cannot restart the kernel. "
1029 1029 "No previous call to 'start_kernel'.")
1030 1030 else:
1031 1031 # Stop currently running kernel.
1032 1032 self.shutdown_kernel(now=now, restart=True)
1033 1033
1034 1034 # Start new kernel.
1035 1035 self._launch_args.update(kw)
1036 1036 self.start_kernel(**self._launch_args)
1037 1037
1038 1038 # FIXME: Messages get dropped in Windows due to probable ZMQ bug
1039 1039 # unless there is some delay here.
1040 1040 if sys.platform == 'win32':
1041 1041 time.sleep(0.2)
1042 1042
1043 1043 @property
1044 1044 def has_kernel(self):
1045 1045 """Has a kernel been started that we are managing."""
1046 1046 return self.kernel is not None
1047 1047
1048 1048 def _kill_kernel(self):
1049 1049 """Kill the running kernel.
1050 1050
1051 1051 This is a private method, callers should use shutdown_kernel(now=True).
1052 1052 """
1053 1053 if self.has_kernel:
1054 1054 # Pause the heart beat channel if it exists.
1055 1055 if self._hb_channel is not None:
1056 1056 self._hb_channel.pause()
1057 1057
1058 1058 # Signal the kernel to terminate (sends SIGKILL on Unix and calls
1059 1059 # TerminateProcess() on Win32).
1060 1060 try:
1061 1061 self.kernel.kill()
1062 1062 except OSError as e:
1063 1063 # In Windows, we will get an Access Denied error if the process
1064 1064 # has already terminated. Ignore it.
1065 1065 if sys.platform == 'win32':
1066 1066 if e.winerror != 5:
1067 1067 raise
1068 1068 # On Unix, we may get an ESRCH error if the process has already
1069 1069 # terminated. Ignore it.
1070 1070 else:
1071 1071 from errno import ESRCH
1072 1072 if e.errno != ESRCH:
1073 1073 raise
1074 1074
1075 1075 # Block until the kernel terminates.
1076 1076 self.kernel.wait()
1077 1077 self.kernel = None
1078 1078 else:
1079 1079 raise RuntimeError("Cannot kill kernel. No kernel is running!")
1080 1080
1081 1081 def interrupt_kernel(self):
1082 1082 """Interrupts the kernel by sending it a signal.
1083 1083
1084 1084 Unlike ``signal_kernel``, this operation is well supported on all
1085 1085 platforms.
1086 1086 """
1087 1087 if self.has_kernel:
1088 1088 if sys.platform == 'win32':
1089 1089 from parentpoller import ParentPollerWindows as Poller
1090 1090 Poller.send_interrupt(self.kernel.win32_interrupt_event)
1091 1091 else:
1092 1092 self.kernel.send_signal(signal.SIGINT)
1093 1093 else:
1094 1094 raise RuntimeError("Cannot interrupt kernel. No kernel is running!")
1095 1095
1096 1096 def signal_kernel(self, signum):
1097 1097 """Sends a signal to the kernel.
1098 1098
1099 1099 Note that since only SIGTERM is supported on Windows, this function is
1100 1100 only useful on Unix systems.
1101 1101 """
1102 1102 if self.has_kernel:
1103 1103 self.kernel.send_signal(signum)
1104 1104 else:
1105 1105 raise RuntimeError("Cannot signal kernel. No kernel is running!")
1106 1106
1107 1107 @property
1108 1108 def is_alive(self):
1109 1109 """Is the kernel process still running?"""
1110 1110 if self.has_kernel:
1111 1111 if self.kernel.poll() is None:
1112 1112 return True
1113 1113 else:
1114 1114 return False
1115 1115 elif self._hb_channel is not None:
1116 1116 # We didn't start the kernel with this KernelManager so we
1117 1117 # use the heartbeat.
1118 1118 return self._hb_channel.is_beating()
1119 1119 else:
1120 1120 # no heartbeat and not local, we can't tell if it's running,
1121 1121 # so naively return True
1122 1122 return True
1123 1123
1124 1124
1125 1125 #-----------------------------------------------------------------------------
1126 1126 # ABC Registration
1127 1127 #-----------------------------------------------------------------------------
1128 1128
1129 1129 ShellChannelABC.register(ShellChannel)
1130 1130 IOPubChannelABC.register(IOPubChannel)
1131 1131 HBChannelABC.register(HBChannel)
1132 1132 StdInChannelABC.register(StdInChannel)
1133 1133 KernelManagerABC.register(KernelManager)
1134 1134
General Comments 0
You need to be logged in to leave comments. Login now