##// END OF EJS Templates
Merge branch 'mynewkernel' into upstream-newkernel
Brian Granger -
r2911:49041c42 merge
parent child Browse files
Show More
@@ -0,0 +1,43 b''
1 """The client and server for a basic ping-pong style heartbeat.
2 """
3
4 #-----------------------------------------------------------------------------
5 # Copyright (C) 2008-2010 The IPython Development Team
6 #
7 # Distributed under the terms of the BSD License. The full license is in
8 # the file COPYING, distributed as part of this software.
9 #-----------------------------------------------------------------------------
10
11 #-----------------------------------------------------------------------------
12 # Imports
13 #-----------------------------------------------------------------------------
14
15 import sys
16 from threading import Thread
17
18 import zmq
19
20 #-----------------------------------------------------------------------------
21 # Code
22 #-----------------------------------------------------------------------------
23
24
25 class Heartbeat(Thread):
26 "A simple ping-pong style heartbeat that runs in a thread."
27
28 def __init__(self, context, addr=('127.0.0.1', 0)):
29 Thread.__init__(self)
30 self.context = context
31 self.addr = addr
32 self.ip = addr[0]
33 self.port = addr[1]
34 self.daemon = True
35
36 def run(self):
37 self.socket = self.context.socket(zmq.REP)
38 if self.port == 0:
39 self.port = self.socket.bind_to_random_port('tcp://%s' % self.ip)
40 else:
41 self.socket.bind('tcp://%s:%i' % self.addr)
42 zmq.device(zmq.FORWARDER, self.socket, self.socket)
43
@@ -31,8 +31,9 b' class BaseFrontendMixin(object):'
31 # Disconnect the old kernel manager's channels.
31 # Disconnect the old kernel manager's channels.
32 old_manager.sub_channel.message_received.disconnect(self._dispatch)
32 old_manager.sub_channel.message_received.disconnect(self._dispatch)
33 old_manager.xreq_channel.message_received.disconnect(self._dispatch)
33 old_manager.xreq_channel.message_received.disconnect(self._dispatch)
34 old_manager.rep_channel.message_received.connect(self._dispatch)
34 old_manager.rep_channel.message_received.disconnect(self._dispatch)
35
35 old_manager.hb_channel.kernel_died.disconnect(self._handle_kernel_died)
36
36 # Handle the case where the old kernel manager is still listening.
37 # Handle the case where the old kernel manager is still listening.
37 if old_manager.channels_running:
38 if old_manager.channels_running:
38 self._stopped_channels()
39 self._stopped_channels()
@@ -50,7 +51,8 b' class BaseFrontendMixin(object):'
50 kernel_manager.sub_channel.message_received.connect(self._dispatch)
51 kernel_manager.sub_channel.message_received.connect(self._dispatch)
51 kernel_manager.xreq_channel.message_received.connect(self._dispatch)
52 kernel_manager.xreq_channel.message_received.connect(self._dispatch)
52 kernel_manager.rep_channel.message_received.connect(self._dispatch)
53 kernel_manager.rep_channel.message_received.connect(self._dispatch)
53
54 kernel_manager.hb_channel.kernel_died.connect(self._handle_kernel_died)
55
54 # Handle the case where the kernel manager started channels before
56 # Handle the case where the kernel manager started channels before
55 # we connected.
57 # we connected.
56 if kernel_manager.channels_running:
58 if kernel_manager.channels_running:
@@ -91,3 +93,17 b' class BaseFrontendMixin(object):'
91 """
93 """
92 session = self._kernel_manager.session.session
94 session = self._kernel_manager.session.session
93 return msg['parent_header']['session'] == session
95 return msg['parent_header']['session'] == session
96
97 def _handle_kernel_died(self, since_last_heartbeat):
98 """ This is called when the ``kernel_died`` signal is emitted.
99
100 This method is called when the kernel heartbeat has not been
101 active for a certain amount of time. The typical action will be to
102 give the user the option of restarting the kernel.
103
104 Parameters
105 ----------
106 since_last_heartbeat : float
107 The time since the heartbeat was last received.
108 """
109 pass
@@ -90,6 +90,7 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
90
90
91 # Protected class variables.
91 # Protected class variables.
92 _input_splitter_class = InputSplitter
92 _input_splitter_class = InputSplitter
93 _possible_kernel_restart = Bool(False)
93
94
94 #---------------------------------------------------------------------------
95 #---------------------------------------------------------------------------
95 # 'object' interface
96 # 'object' interface
@@ -176,7 +177,8 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
176 self._kernel_interrupt()
177 self._kernel_interrupt()
177 return True
178 return True
178 elif key == QtCore.Qt.Key_Period:
179 elif key == QtCore.Qt.Key_Period:
179 self._kernel_restart()
180 message = 'Are you sure you want to restart the kernel?'
181 self._kernel_restart(message)
180 return True
182 return True
181 return super(FrontendWidget, self)._event_filter_console_keypress(event)
183 return super(FrontendWidget, self)._event_filter_console_keypress(event)
182
184
@@ -348,29 +350,38 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
348 self._append_plain_text('Kernel process is either remote or '
350 self._append_plain_text('Kernel process is either remote or '
349 'unspecified. Cannot interrupt.\n')
351 'unspecified. Cannot interrupt.\n')
350
352
351 def _kernel_restart(self):
353 def _kernel_restart(self, message):
352 """ Attempts to restart the running kernel.
354 """ Attempts to restart the running kernel.
353 """
355 """
354 if self.custom_restart:
356 # We want to make sure that if this dialog is already happening, that
355 self.custom_restart_requested.emit()
357 # other signals don't trigger it again. This can happen when the
356 elif self.kernel_manager.has_kernel:
358 # kernel_died heartbeat signal is emitted and the user is slow to
357 message = 'Are you sure you want to restart the kernel?'
359 # respond to the dialog.
358 buttons = QtGui.QMessageBox.Yes | QtGui.QMessageBox.No
360 if not self._possible_kernel_restart:
359 result = QtGui.QMessageBox.question(self, 'Restart kernel?',
361 if self.custom_restart:
360 message, buttons)
362 self.custom_restart_requested.emit()
361 if result == QtGui.QMessageBox.Yes:
363 elif self.kernel_manager.has_kernel:
362 try:
364 # Setting this to True will prevent this logic from happening
363 self.kernel_manager.restart_kernel()
365 # again until the current pass is completed.
364 except RuntimeError:
366 self._possible_kernel_restart = True
365 message = 'Kernel started externally. Cannot restart.\n'
367 buttons = QtGui.QMessageBox.Yes | QtGui.QMessageBox.No
366 self._append_plain_text(message)
368 result = QtGui.QMessageBox.question(self, 'Restart kernel?',
367 else:
369 message, buttons)
368 self._stopped_channels()
370 if result == QtGui.QMessageBox.Yes:
369 self._append_plain_text('Kernel restarting...\n')
371 try:
370 self._show_interpreter_prompt()
372 self.kernel_manager.restart_kernel()
371 else:
373 except RuntimeError:
372 self._append_plain_text('Kernel process is either remote or '
374 message = 'Kernel started externally. Cannot restart.\n'
373 'unspecified. Cannot restart.\n')
375 self._append_plain_text(message)
376 else:
377 self._stopped_channels()
378 self._append_plain_text('Kernel restarting...\n')
379 self._show_interpreter_prompt()
380 # This might need to be moved to another location?
381 self._possible_kernel_restart = False
382 else:
383 self._append_plain_text('Kernel process is either remote or '
384 'unspecified. Cannot restart.\n')
374
385
375 def _process_execute_abort(self, msg):
386 def _process_execute_abort(self, msg):
376 """ Process a reply for an aborted execution request.
387 """ Process a reply for an aborted execution request.
@@ -154,6 +154,14 b' class IPythonWidget(FrontendWidget):'
154 # FIXME: Disabled until history requests are properly implemented.
154 # FIXME: Disabled until history requests are properly implemented.
155 #self.kernel_manager.xreq_channel.history(raw=True, output=False)
155 #self.kernel_manager.xreq_channel.history(raw=True, output=False)
156
156
157 def _handle_kernel_died(self, since_last_heartbeat):
158 """ Handle the kernel's death by asking if the user wants to restart.
159 """
160 message = 'The kernel heartbeat has been inactive for %.2f ' \
161 'seconds. Do you want to restart the kernel? You may ' \
162 'first want to check the network connection.' % since_last_heartbeat
163 self._kernel_restart(message)
164
157 #---------------------------------------------------------------------------
165 #---------------------------------------------------------------------------
158 # 'FrontendWidget' interface
166 # 'FrontendWidget' interface
159 #---------------------------------------------------------------------------
167 #---------------------------------------------------------------------------
@@ -33,6 +33,8 b' def main():'
33 help='set the SUB channel port [default random]')
33 help='set the SUB channel port [default random]')
34 kgroup.add_argument('--rep', type=int, metavar='PORT', default=0,
34 kgroup.add_argument('--rep', type=int, metavar='PORT', default=0,
35 help='set the REP channel port [default random]')
35 help='set the REP channel port [default random]')
36 kgroup.add_argument('--hb', type=int, metavar='PORT', default=0,
37 help='set the heartbeat port [default: random]')
36
38
37 egroup = kgroup.add_mutually_exclusive_group()
39 egroup = kgroup.add_mutually_exclusive_group()
38 egroup.add_argument('--pure', action='store_true', help = \
40 egroup.add_argument('--pure', action='store_true', help = \
@@ -61,7 +63,8 b' def main():'
61 # Create a KernelManager and start a kernel.
63 # Create a KernelManager and start a kernel.
62 kernel_manager = QtKernelManager(xreq_address=(args.ip, args.xreq),
64 kernel_manager = QtKernelManager(xreq_address=(args.ip, args.xreq),
63 sub_address=(args.ip, args.sub),
65 sub_address=(args.ip, args.sub),
64 rep_address=(args.ip, args.rep))
66 rep_address=(args.ip, args.rep),
67 hb_address=(args.ip, args.hb))
65 if args.ip == LOCALHOST and not args.existing:
68 if args.ip == LOCALHOST and not args.existing:
66 if args.pure:
69 if args.pure:
67 kernel_manager.start_kernel(ipython=False)
70 kernel_manager.start_kernel(ipython=False)
@@ -8,7 +8,7 b' import zmq'
8 # IPython imports.
8 # IPython imports.
9 from IPython.utils.traitlets import Type
9 from IPython.utils.traitlets import Type
10 from IPython.zmq.kernelmanager import KernelManager, SubSocketChannel, \
10 from IPython.zmq.kernelmanager import KernelManager, SubSocketChannel, \
11 XReqSocketChannel, RepSocketChannel
11 XReqSocketChannel, RepSocketChannel, HBSocketChannel
12 from util import MetaQObjectHasTraits
12 from util import MetaQObjectHasTraits
13
13
14 # When doing multiple inheritance from QtCore.QObject and other classes
14 # When doing multiple inheritance from QtCore.QObject and other classes
@@ -149,6 +149,32 b' class QtRepSocketChannel(RepSocketChannel, QtCore.QObject):'
149 self.input_requested.emit(msg)
149 self.input_requested.emit(msg)
150
150
151
151
152 class QtHBSocketChannel(HBSocketChannel, QtCore.QObject):
153
154 # Emitted when the kernel has died.
155 kernel_died = QtCore.pyqtSignal(object)
156
157 #---------------------------------------------------------------------------
158 # 'object' interface
159 #---------------------------------------------------------------------------
160
161 def __init__(self, *args, **kw):
162 """ Reimplemented to ensure that QtCore.QObject is initialized first.
163 """
164 QtCore.QObject.__init__(self)
165 HBSocketChannel.__init__(self, *args, **kw)
166
167 #---------------------------------------------------------------------------
168 # 'RepSocketChannel' interface
169 #---------------------------------------------------------------------------
170
171 def call_handlers(self, since_last_heartbeat):
172 """ Reimplemented to emit signals instead of making callbacks.
173 """
174 # Emit the generic signal.
175 self.kernel_died.emit(since_last_heartbeat)
176
177
152 class QtKernelManager(KernelManager, QtCore.QObject):
178 class QtKernelManager(KernelManager, QtCore.QObject):
153 """ A KernelManager that provides signals and slots.
179 """ A KernelManager that provides signals and slots.
154 """
180 """
@@ -165,6 +191,7 b' class QtKernelManager(KernelManager, QtCore.QObject):'
165 sub_channel_class = Type(QtSubSocketChannel)
191 sub_channel_class = Type(QtSubSocketChannel)
166 xreq_channel_class = Type(QtXReqSocketChannel)
192 xreq_channel_class = Type(QtXReqSocketChannel)
167 rep_channel_class = Type(QtRepSocketChannel)
193 rep_channel_class = Type(QtRepSocketChannel)
194 hb_channel_class = Type(QtHBSocketChannel)
168
195
169 #---------------------------------------------------------------------------
196 #---------------------------------------------------------------------------
170 # 'object' interface
197 # 'object' interface
@@ -19,7 +19,7 b' from exitpoller import ExitPollerUnix, ExitPollerWindows'
19 from displayhook import DisplayHook
19 from displayhook import DisplayHook
20 from iostream import OutStream
20 from iostream import OutStream
21 from session import Session
21 from session import Session
22
22 from heartbeat import Heartbeat
23
23
24 def bind_port(socket, ip, port):
24 def bind_port(socket, ip, port):
25 """ Binds the specified ZMQ socket. If the port is zero, a random port is
25 """ Binds the specified ZMQ socket. If the port is zero, a random port is
@@ -47,6 +47,8 b' def make_argument_parser():'
47 help='set the PUB channel port [default: random]')
47 help='set the PUB channel port [default: random]')
48 parser.add_argument('--req', type=int, metavar='PORT', default=0,
48 parser.add_argument('--req', type=int, metavar='PORT', default=0,
49 help='set the REQ channel port [default: random]')
49 help='set the REQ channel port [default: random]')
50 parser.add_argument('--hb', type=int, metavar='PORT', default=0,
51 help='set the heartbeat port [default: random]')
50
52
51 if sys.platform == 'win32':
53 if sys.platform == 'win32':
52 parser.add_argument('--parent', type=int, metavar='HANDLE',
54 parser.add_argument('--parent', type=int, metavar='HANDLE',
@@ -84,6 +86,10 b' def make_kernel(namespace, kernel_factory,'
84 req_port = bind_port(req_socket, namespace.ip, namespace.req)
86 req_port = bind_port(req_socket, namespace.ip, namespace.req)
85 io.raw_print("REQ Channel on port", req_port)
87 io.raw_print("REQ Channel on port", req_port)
86
88
89 hb = Heartbeat(context, (namespace.ip, namespace.hb))
90 hb.start()
91 io.raw_print("Heartbeat REP Channel on port", hb.port)
92
87 # Redirect input streams and set a display hook.
93 # Redirect input streams and set a display hook.
88 if out_stream_factory:
94 if out_stream_factory:
89 pass
95 pass
@@ -122,7 +128,7 b' def make_default_main(kernel_factory):'
122 return main
128 return main
123
129
124
130
125 def base_launch_kernel(code, xrep_port=0, pub_port=0, req_port=0,
131 def base_launch_kernel(code, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
126 independent=False, extra_arguments=[]):
132 independent=False, extra_arguments=[]):
127 """ Launches a localhost kernel, binding to the specified ports.
133 """ Launches a localhost kernel, binding to the specified ports.
128
134
@@ -140,6 +146,9 b' def base_launch_kernel(code, xrep_port=0, pub_port=0, req_port=0,'
140 req_port : int, optional
146 req_port : int, optional
141 The port to use for the REQ (raw input) channel.
147 The port to use for the REQ (raw input) channel.
142
148
149 hb_port : int, optional
150 The port to use for the hearbeat REP channel.
151
143 independent : bool, optional (default False)
152 independent : bool, optional (default False)
144 If set, the kernel process is guaranteed to survive if this process
153 If set, the kernel process is guaranteed to survive if this process
145 dies. If not set, an effort is made to ensure that the kernel is killed
154 dies. If not set, an effort is made to ensure that the kernel is killed
@@ -157,7 +166,8 b' def base_launch_kernel(code, xrep_port=0, pub_port=0, req_port=0,'
157 """
166 """
158 # Find open ports as necessary.
167 # Find open ports as necessary.
159 ports = []
168 ports = []
160 ports_needed = int(xrep_port <= 0) + int(pub_port <= 0) + int(req_port <= 0)
169 ports_needed = int(xrep_port <= 0) + int(pub_port <= 0) + \
170 int(req_port <= 0) + int(hb_port <= 0)
161 for i in xrange(ports_needed):
171 for i in xrange(ports_needed):
162 sock = socket.socket()
172 sock = socket.socket()
163 sock.bind(('', 0))
173 sock.bind(('', 0))
@@ -172,10 +182,13 b' def base_launch_kernel(code, xrep_port=0, pub_port=0, req_port=0,'
172 pub_port = ports.pop(0)
182 pub_port = ports.pop(0)
173 if req_port <= 0:
183 if req_port <= 0:
174 req_port = ports.pop(0)
184 req_port = ports.pop(0)
185 if hb_port <= 0:
186 hb_port = ports.pop(0)
175
187
176 # Build the kernel launch command.
188 # Build the kernel launch command.
177 arguments = [ sys.executable, '-c', code, '--xrep', str(xrep_port),
189 arguments = [ sys.executable, '-c', code, '--xrep', str(xrep_port),
178 '--pub', str(pub_port), '--req', str(req_port) ]
190 '--pub', str(pub_port), '--req', str(req_port),
191 '--hb', str(hb_port) ]
179 arguments.extend(extra_arguments)
192 arguments.extend(extra_arguments)
180
193
181 # Spawn a kernel.
194 # Spawn a kernel.
@@ -196,4 +209,4 b' def base_launch_kernel(code, xrep_port=0, pub_port=0, req_port=0,'
196 else:
209 else:
197 proc = Popen(arguments + ['--parent'])
210 proc = Popen(arguments + ['--parent'])
198
211
199 return proc, xrep_port, pub_port, req_port
212 return proc, xrep_port, pub_port, req_port, hb_port
@@ -377,8 +377,8 b' class TkKernel(Kernel):'
377 # Kernel main and launch functions
377 # Kernel main and launch functions
378 #-----------------------------------------------------------------------------
378 #-----------------------------------------------------------------------------
379
379
380 def launch_kernel(xrep_port=0, pub_port=0, req_port=0, independent=False,
380 def launch_kernel(xrep_port=0, pub_port=0, req_port=0, hb_port=0,
381 pylab=False):
381 independent=False, pylab=False):
382 """ Launches a localhost kernel, binding to the specified ports.
382 """ Launches a localhost kernel, binding to the specified ports.
383
383
384 Parameters
384 Parameters
@@ -392,6 +392,9 b' def launch_kernel(xrep_port=0, pub_port=0, req_port=0, independent=False,'
392 req_port : int, optional
392 req_port : int, optional
393 The port to use for the REQ (raw input) channel.
393 The port to use for the REQ (raw input) channel.
394
394
395 hb_port : int, optional
396 The port to use for the hearbeat REP channel.
397
395 independent : bool, optional (default False)
398 independent : bool, optional (default False)
396 If set, the kernel process is guaranteed to survive if this process
399 If set, the kernel process is guaranteed to survive if this process
397 dies. If not set, an effort is made to ensure that the kernel is killed
400 dies. If not set, an effort is made to ensure that the kernel is killed
@@ -415,8 +418,8 b' def launch_kernel(xrep_port=0, pub_port=0, req_port=0, independent=False,'
415 if isinstance(pylab, basestring):
418 if isinstance(pylab, basestring):
416 extra_arguments.append(pylab)
419 extra_arguments.append(pylab)
417 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
420 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
418 xrep_port, pub_port, req_port, independent,
421 xrep_port, pub_port, req_port, hb_port,
419 extra_arguments)
422 independent, extra_arguments)
420
423
421 def main():
424 def main():
422 """ The IPython kernel main entry point.
425 """ The IPython kernel main entry point.
@@ -447,6 +447,71 b' class RepSocketChannel(ZmqSocketChannel):'
447 self.add_io_state(POLLOUT)
447 self.add_io_state(POLLOUT)
448
448
449
449
450 class HBSocketChannel(ZmqSocketChannel):
451 """The heartbeat channel which monitors the kernel heartbeat.
452 """
453
454 time_to_dead = 5.0
455 socket = None
456 poller = None
457
458 def __init__(self, context, session, address):
459 super(HBSocketChannel, self).__init__(context, session, address)
460
461 def _create_socket(self):
462 self.socket = self.context.socket(zmq.REQ)
463 self.socket.setsockopt(zmq.IDENTITY, self.session.session)
464 self.socket.connect('tcp://%s:%i' % self.address)
465 self.poller = zmq.Poller()
466 self.poller.register(self.socket, zmq.POLLIN)
467
468 def run(self):
469 """The thread's main activity. Call start() instead."""
470 self._create_socket()
471
472 while True:
473 since_last_heartbeat = 0.0
474 request_time = time.time()
475 try:
476 self.socket.send_json('ping')
477 except zmq.ZMQError, e:
478 if e.errno == zmq.EFSM:
479 time.sleep(self.time_to_dead)
480 self._create_socket()
481 else:
482 raise
483 else:
484 while True:
485 try:
486 reply = self.socket.recv_json(zmq.NOBLOCK)
487 except zmq.ZMQError, e:
488 if e.errno == zmq.EAGAIN:
489 until_dead = self.time_to_dead-(time.time()-request_time)
490 self.poller.poll(until_dead)
491 since_last_heartbeat = time.time() - request_time
492 if since_last_heartbeat > self.time_to_dead:
493 self.call_handlers(since_last_heartbeat)
494 break
495 else:
496 # We should probably log this instead
497 raise
498 else:
499 until_dead = self.time_to_dead-(time.time()-request_time)
500 if until_dead > 0.0:
501 time.sleep(until_dead)
502 break
503
504 def call_handlers(self, since_last_heartbeat):
505 """This method is called in the ioloop thread when a message arrives.
506
507 Subclasses should override this method to handle incoming messages.
508 It is important to remember that this method is called in the thread
509 so that some logic must be done to ensure that the application leve
510 handlers are called in the application thread.
511 """
512 raise NotImplementedError('call_handlers must be defined in a subclass.')
513
514
450 #-----------------------------------------------------------------------------
515 #-----------------------------------------------------------------------------
451 # Main kernel manager class
516 # Main kernel manager class
452 #-----------------------------------------------------------------------------
517 #-----------------------------------------------------------------------------
@@ -475,17 +540,20 b' class KernelManager(HasTraits):'
475 xreq_address = TCPAddress((LOCALHOST, 0))
540 xreq_address = TCPAddress((LOCALHOST, 0))
476 sub_address = TCPAddress((LOCALHOST, 0))
541 sub_address = TCPAddress((LOCALHOST, 0))
477 rep_address = TCPAddress((LOCALHOST, 0))
542 rep_address = TCPAddress((LOCALHOST, 0))
543 hb_address = TCPAddress((LOCALHOST, 0))
478
544
479 # The classes to use for the various channels.
545 # The classes to use for the various channels.
480 xreq_channel_class = Type(XReqSocketChannel)
546 xreq_channel_class = Type(XReqSocketChannel)
481 sub_channel_class = Type(SubSocketChannel)
547 sub_channel_class = Type(SubSocketChannel)
482 rep_channel_class = Type(RepSocketChannel)
548 rep_channel_class = Type(RepSocketChannel)
549 hb_channel_class = Type(HBSocketChannel)
483
550
484 # Protected traits.
551 # Protected traits.
485 _launch_args = Any
552 _launch_args = Any
486 _xreq_channel = Any
553 _xreq_channel = Any
487 _sub_channel = Any
554 _sub_channel = Any
488 _rep_channel = Any
555 _rep_channel = Any
556 _hb_channel = Any
489
557
490 #--------------------------------------------------------------------------
558 #--------------------------------------------------------------------------
491 # Channel management methods:
559 # Channel management methods:
@@ -502,6 +570,7 b' class KernelManager(HasTraits):'
502 self.xreq_channel.start()
570 self.xreq_channel.start()
503 self.sub_channel.start()
571 self.sub_channel.start()
504 self.rep_channel.start()
572 self.rep_channel.start()
573 self.hb_channel.start()
505
574
506 def stop_channels(self):
575 def stop_channels(self):
507 """Stops the channels for this kernel.
576 """Stops the channels for this kernel.
@@ -512,13 +581,15 b' class KernelManager(HasTraits):'
512 self.xreq_channel.stop()
581 self.xreq_channel.stop()
513 self.sub_channel.stop()
582 self.sub_channel.stop()
514 self.rep_channel.stop()
583 self.rep_channel.stop()
584 self.hb_channel.stop()
515
585
516 @property
586 @property
517 def channels_running(self):
587 def channels_running(self):
518 """Are all of the channels created and running?"""
588 """Are all of the channels created and running?"""
519 return self.xreq_channel.is_alive() \
589 return self.xreq_channel.is_alive() \
520 and self.sub_channel.is_alive() \
590 and self.sub_channel.is_alive() \
521 and self.rep_channel.is_alive()
591 and self.rep_channel.is_alive() \
592 and self.hb_channel.is_alive()
522
593
523 #--------------------------------------------------------------------------
594 #--------------------------------------------------------------------------
524 # Kernel process management methods:
595 # Kernel process management methods:
@@ -535,8 +606,9 b' class KernelManager(HasTraits):'
535 ipython : bool, optional (default True)
606 ipython : bool, optional (default True)
536 Whether to use an IPython kernel instead of a plain Python kernel.
607 Whether to use an IPython kernel instead of a plain Python kernel.
537 """
608 """
538 xreq, sub, rep = self.xreq_address, self.sub_address, self.rep_address
609 xreq, sub, rep, hb = self.xreq_address, self.sub_address, \
539 if xreq[0] != LOCALHOST or sub[0] != LOCALHOST or rep[0] != LOCALHOST:
610 self.rep_address, self.hb_address
611 if xreq[0] != LOCALHOST or sub[0] != LOCALHOST or rep[0] != LOCALHOST or hb[0] != LOCALHOST:
540 raise RuntimeError("Can only launch a kernel on localhost."
612 raise RuntimeError("Can only launch a kernel on localhost."
541 "Make sure that the '*_address' attributes are "
613 "Make sure that the '*_address' attributes are "
542 "configured properly.")
614 "configured properly.")
@@ -546,11 +618,13 b' class KernelManager(HasTraits):'
546 from ipkernel import launch_kernel as launch
618 from ipkernel import launch_kernel as launch
547 else:
619 else:
548 from pykernel import launch_kernel as launch
620 from pykernel import launch_kernel as launch
549 self.kernel, xrep, pub, req = launch(xrep_port=xreq[1], pub_port=sub[1],
621 self.kernel, xrep, pub, req, hb = launch(
550 req_port=rep[1], **kw)
622 xrep_port=xreq[1], pub_port=sub[1], req_port=rep[1],
623 hb_port=hb[1], **kw)
551 self.xreq_address = (LOCALHOST, xrep)
624 self.xreq_address = (LOCALHOST, xrep)
552 self.sub_address = (LOCALHOST, pub)
625 self.sub_address = (LOCALHOST, pub)
553 self.rep_address = (LOCALHOST, req)
626 self.rep_address = (LOCALHOST, req)
627 self.hb_address = (LOCALHOST, hb)
554
628
555 def restart_kernel(self):
629 def restart_kernel(self):
556 """Restarts a kernel with the same arguments that were used to launch
630 """Restarts a kernel with the same arguments that were used to launch
@@ -630,3 +704,12 b' class KernelManager(HasTraits):'
630 self.session,
704 self.session,
631 self.rep_address)
705 self.rep_address)
632 return self._rep_channel
706 return self._rep_channel
707
708 @property
709 def hb_channel(self):
710 """Get the REP socket channel object to handle stdin (raw_input)."""
711 if self._hb_channel is None:
712 self._hb_channel = self.hb_channel_class(self.context,
713 self.session,
714 self.hb_address)
715 return self._hb_channel
General Comments 0
You need to be logged in to leave comments. Login now