diff --git a/IPython/html/static/notebook/js/notificationarea.js b/IPython/html/static/notebook/js/notificationarea.js
index e3b1e9e..4608436 100644
--- a/IPython/html/static/notebook/js/notificationarea.js
+++ b/IPython/html/static/notebook/js/notificationarea.js
@@ -141,18 +141,24 @@ define([
knw.set_message("Restarting kernel", 2000);
});
- this.events.on('status_autorestarting.Kernel', function () {
- dialog.modal({
- notebook: that.notebook,
- keyboard_manager: that.keyboard_manager,
- title: "Kernel Restarting",
- body: "The kernel appears to have died. It will restart automatically.",
- buttons: {
- OK : {
- class : "btn-primary"
+ this.events.on('status_autorestarting.Kernel', function (evt, info) {
+ // only show the dialog on the first restart attempt
+ if (info.attempt == 1) {
+ // hide existing modal dialog
+ $(".modal").modal('hide');
+
+ dialog.modal({
+ notebook: that.notebook,
+ keyboard_manager: that.keyboard_manager,
+ title: "Kernel Restarting",
+ body: "The kernel appears to have died. It will restart automatically.",
+ buttons: {
+ OK : {
+ class : "btn-primary"
+ }
}
- }
- });
+ });
+ };
that.save_widget.update_document_title();
knw.danger("Dead kernel");
@@ -174,9 +180,13 @@ define([
});
this.events.on('connection_failed.Kernel', function () {
+ // hide existing dialog
+ $(".modal").modal('hide');
+
var msg = "A WebSocket connection could not be established." +
" You will NOT be able to run code. Check your" +
" network connection or notebook server configuration.";
+
dialog.modal({
title: "WebSocket connection failed",
body: msg,
@@ -193,34 +203,48 @@ define([
});
});
- this.events.on('kernel_dead.Kernel status_killed.Kernel status_killed.Session', function () {
+ this.events.on('status_killed.Kernel status_killed.Session', function () {
that.save_widget.update_document_title();
knw.danger("Dead kernel");
$kernel_ind_icon.attr('class','kernel_dead_icon').attr('title','Kernel Dead');
});
this.events.on('kernel_dead.Kernel', function () {
- var msg = 'The kernel has died, and the automatic restart has failed.' +
- ' It is possible the kernel cannot be restarted.' +
- ' If you are not able to restart the kernel, you will still be able to save' +
- ' the notebook, but running code will no longer work until the notebook' +
- ' is reopened.';
- dialog.modal({
- title: "Dead kernel",
- body : msg,
- keyboard_manager: that.keyboard_manager,
- notebook: that.notebook,
- buttons : {
- "Manual Restart": {
- class: "btn-danger",
- click: function () {
- that.notebook.start_session();
- }
- },
+ var showMsg = function () {
+ // hide existing dialog
+ $(".modal").modal('hide');
+
+ var msg = 'The kernel has died, and the automatic restart has failed.' +
+ ' It is possible the kernel cannot be restarted.' +
+ ' If you are not able to restart the kernel, you will still be able to save' +
+ ' the notebook, but running code will no longer work until the notebook' +
+ ' is reopened.';
+
+ dialog.modal({
+ title: "Dead kernel",
+ body : msg,
+ keyboard_manager: that.keyboard_manager,
+ notebook: that.notebook,
+ buttons : {
+ "Manual Restart": {
+ class: "btn-danger",
+ click: function () {
+ that.notebook.start_session();
+ }
+ },
"Don't restart": {}
- }
- });
+ }
+ });
+
+ return false;
+ };
+
+ that.save_widget.update_document_title();
+ knw.danger("Dead kernel", undefined, showMsg);
+ $kernel_ind_icon.attr('class','kernel_dead_icon').attr('title','Kernel Dead');
+
+ showMsg();
});
this.events.on('kernel_dead.Session', function (evt, info) {
@@ -246,6 +270,9 @@ define([
cm_open = $.proxy(cm.refresh, cm);
}
+ // hide existing modal dialog
+ $(".modal").modal('hide');
+
dialog.modal({
title: "Failed to start the kernel",
body : msg,
diff --git a/IPython/html/static/services/kernels/js/kernel.js b/IPython/html/static/services/kernels/js/kernel.js
index 7aaadf0..4e60d09 100644
--- a/IPython/html/static/services/kernels/js/kernel.js
+++ b/IPython/html/static/services/kernels/js/kernel.js
@@ -61,6 +61,8 @@ define([
this.last_msg_id = null;
this.last_msg_callbacks = {};
+
+ this._autorestart_attempt = 0;
};
/**
@@ -110,6 +112,10 @@ define([
this.events.on('status_ready.Kernel', record_status);
this.events.on('status_killed.Kernel', record_status);
this.events.on('kernel_dead.Kernel', record_status);
+
+ this.events.on('status_ready.Kernel', function () {
+ that._autorestart_attempt = 0;
+ });
};
/**
@@ -922,8 +928,9 @@ define([
// in that it means the kernel died and the server is restarting it.
// status_restarting sets the notification widget,
// autorestart shows the more prominent dialog.
+ this._autorestart_attempt = this._autorestart_attempt + 1;
this.events.trigger('status_restarting.Kernel', {kernel: this});
- this.events.trigger('status_autorestarting.Kernel', {kernel: this});
+ this.events.trigger('status_autorestarting.Kernel', {kernel: this, attempt: this._autorestart_attempt});
} else if (execution_state === 'dead') {
this.events.trigger('kernel_dead.Kernel', {kernel: this});
diff --git a/IPython/html/tests/services/kernel.js b/IPython/html/tests/services/kernel.js
index 48e889e..bba1c28 100644
--- a/IPython/html/tests/services/kernel.js
+++ b/IPython/html/tests/services/kernel.js
@@ -217,12 +217,6 @@ casper.notebook_test(function () {
// wait for any last idle/busy messages to be handled
this.wait_for_kernel_ready();
- // TODO: test for failed restart, that it triggers
- // kernel_dead.Kernel? How to do this?
-
- // TODO: test for status_autorestarting.Kernel? how to trigger
- // this?
-
// check for events in the interrupt cycle
this.event_test(
'interrupt',
@@ -271,4 +265,53 @@ casper.notebook_test(function () {
});
}
);
+
+ // start the kernel back up
+ this.thenEvaluate(function () {
+ IPython.notebook.kernel.restart();
+ });
+ this.waitFor(this.kernel_running);
+ this.wait_for_kernel_ready();
+
+ // test handling of autorestarting messages
+ this.event_test(
+ 'autorestarting',
+ [
+ 'status_restarting.Kernel',
+ 'status_autorestarting.Kernel',
+ ],
+ function () {
+ this.thenEvaluate(function () {
+ var cell = IPython.notebook.get_cell(0);
+ cell.set_text('import os\n' + 'os._exit(1)');
+ cell.execute();
+ });
+ }
+ );
+ this.wait_for_kernel_ready();
+
+ // test handling of failed restart
+ this.event_test(
+ 'failed_restart',
+ [
+ 'status_restarting.Kernel',
+ 'status_autorestarting.Kernel',
+ 'kernel_dead.Kernel'
+ ],
+ function () {
+ this.thenEvaluate(function () {
+ var cell = IPython.notebook.get_cell(0);
+ cell.set_text("import os\n" +
+ "from IPython.kernel.connect import get_connection_file\n" +
+ "with open(get_connection_file(), 'w') as f:\n" +
+ " f.write('garbage')\n" +
+ "os._exit(1)");
+ cell.execute();
+ });
+ },
+
+ // need an extra-long timeout, because it needs to try
+ // restarting the kernel 5 times!
+ 20000
+ );
});
diff --git a/IPython/html/tests/util.js b/IPython/html/tests/util.js
index b866122..0a99537 100644
--- a/IPython/html/tests/util.js
+++ b/IPython/html/tests/util.js
@@ -584,7 +584,7 @@ casper.dashboard_test = function (test) {
// note that this will only work for UNIQUE events -- if you want to
// listen for the same event twice, this will not work!
-casper.event_test = function (name, events, action) {
+casper.event_test = function (name, events, action, timeout) {
// set up handlers to listen for each of the events
this.thenEvaluate(function (events) {
@@ -611,7 +611,7 @@ casper.event_test = function (name, events, action) {
return this.evaluate(function (events) {
return IPython._events_triggered.length >= events.length;
}, [events]);
- });
+ }, undefined, undefined, timeout);
// test that the events were triggered in the proper order
this.then(function () {