misc notebook: connection file cleanup, first heartbeat, startup flush #1187

Merged
merged 6 commits into from Jan 6, 2012
View
4 IPython/core/shellapp.py
@@ -184,6 +184,10 @@ def init_code(self):
self._run_exec_files()
self._run_cmd_line_code()
+ # flush output, so itwon't be attached to the first cell
+ sys.stdout.flush()
+ sys.stderr.flush()
+
# Hide variables defined here from %who etc.
self.shell.user_ns_hidden.update(self.shell.user_ns)
View
20 IPython/frontend/html/notebook/handlers.py
@@ -18,6 +18,7 @@
import logging
import Cookie
+import time
import uuid
from tornado import web
@@ -412,6 +413,7 @@ def on_first_message(self, msg):
return
km = self.application.kernel_manager
self.time_to_dead = km.time_to_dead
+ self.first_beat = km.first_beat
kernel_id = self.kernel_id
try:
self.iopub_stream = km.create_iopub_stream(kernel_id)
@@ -446,6 +448,7 @@ def start_hb(self, callback):
self._kernel_alive = True
def ping_or_dead():
+ self.hb_stream.flush()
if self._kernel_alive:
self._kernel_alive = False
self.hb_stream.send(b'ping')
@@ -455,25 +458,36 @@ def ping_or_dead():
except:
pass
finally:
- self._hb_periodic_callback.stop()
+ self.stop_hb()
def beat_received(msg):
self._kernel_alive = True
self.hb_stream.on_recv(beat_received)
- self._hb_periodic_callback = ioloop.PeriodicCallback(ping_or_dead, self.time_to_dead*1000)
- self._hb_periodic_callback.start()
+ loop = ioloop.IOLoop.instance()
+ self._hb_periodic_callback = ioloop.PeriodicCallback(ping_or_dead, self.time_to_dead*1000, loop)
+ loop.add_timeout(time.time()+self.first_beat, self._really_start_hb)
self._beating= True
+
+ def _really_start_hb(self):
+ """callback for delayed heartbeat start
+
+ Only start the hb loop if we haven't been closed during the wait.
+ """
+ if self._beating and not self.hb_stream.closed():
+ self._hb_periodic_callback.start()
def stop_hb(self):
"""Stop the heartbeating and cancel all related callbacks."""
if self._beating:
+ self._beating = False
self._hb_periodic_callback.stop()
if not self.hb_stream.closed():
self.hb_stream.on_recv(None)
def kernel_died(self):
self.application.kernel_manager.delete_mapping_for_kernel(self.kernel_id)
+ self.application.log.error("Kernel %s failed to respond to heartbeat", self.kernel_id)
self.write_message(
{'header': {'msg_type': 'status'},
'parent_header': {},
View
3 IPython/frontend/html/notebook/kernelmanager.py
@@ -195,7 +195,10 @@ class MappingKernelManager(MultiKernelManager):
kernel_argv = List(Unicode)
kernel_manager = Instance(KernelManager)
+
time_to_dead = Float(3.0, config=True, help="""Kernel heartbeat interval in seconds.""")
+ first_beat = Float(5.0, config=True, help="Delay (in seconds) before sending first heartbeat.")
+
max_msg_size = Integer(65536, config=True, help="""
The max raw message size accepted from the browser
over a WebSocket connection.
View
42 IPython/frontend/html/notebook/notebookapp.py
@@ -303,9 +303,6 @@ def parse_command_line(self, argv=None):
self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
def init_configurables(self):
- # Don't let Qt or ZMQ swallow KeyboardInterupts.
- signal.signal(signal.SIGINT, signal.SIG_DFL)
-
# force Session default to be secure
default_secure(self.config)
# Create a KernelManager and start a kernel.
@@ -322,11 +319,9 @@ def init_logging(self):
# self.log is a child of. The logging module dipatches log messages to a log
# and all of its ancenstors until propagate is set to False.
self.log.propagate = False
-
- @catch_config_error
- def initialize(self, argv=None):
- super(NotebookApp, self).initialize(argv)
- self.init_configurables()
+
+ def init_webapp(self):
+ """initialize tornado webapp and httpserver"""
self.web_app = NotebookWebApplication(
self, self.kernel_manager, self.notebook_manager, self.log,
self.webapp_settings
@@ -339,7 +334,7 @@ def initialize(self, argv=None):
ssl_options = None
self.web_app.password = self.password
self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options)
- if ssl_options is None and not self.ip:
+ if ssl_options is None and not self.ip and not (self.read_only and not self.password):
self.log.critical('WARNING: the notebook server is listening on all IP addresses '
'but not using any encryption or authentication. This is highly '
'insecure and not recommended.')
@@ -357,6 +352,24 @@ def initialize(self, argv=None):
else:
self.port = port
break
+
+ @catch_config_error
+ def initialize(self, argv=None):
+ super(NotebookApp, self).initialize(argv)
+ self.init_configurables()
+ self.init_webapp()
+
+ def cleanup_kernels(self):
+ """shutdown all kernels
+
+ The kernels will shutdown themselves when this process no longer exists,
+ but explicit shutdown allows the KernelManagers to cleanup the connection files.
+ """
+ self.log.info('Shutting down kernels')
+ km = self.kernel_manager
+ # copy list, since kill_kernel deletes keys
+ for kid in list(km.kernel_ids):
+ km.kill_kernel(kid)
def start(self):
ip = self.ip if self.ip else '[all ip addresses on your system]'
@@ -371,15 +384,20 @@ def start(self):
b = lambda : webbrowser.open("%s://%s:%i" % (proto, ip, self.port),
new=2)
threading.Thread(target=b).start()
-
- ioloop.IOLoop.instance().start()
+ try:
+ ioloop.IOLoop.instance().start()
+ except KeyboardInterrupt:
+ info("Interrupted...")
+ finally:
+ self.cleanup_kernels()
+
#-----------------------------------------------------------------------------
# Main entry point
#-----------------------------------------------------------------------------
def launch_new_instance():
- app = NotebookApp()
+ app = NotebookApp.instance()
app.initialize()
app.start()