diff --git a/IPython/html/static/notebook/js/kernelselector.js b/IPython/html/static/notebook/js/kernelselector.js index 764db72..ce9b919 100644 --- a/IPython/html/static/notebook/js/kernelselector.js +++ b/IPython/html/static/notebook/js/kernelselector.js @@ -54,9 +54,19 @@ define([ return; } var ks = this.kernelspecs[kernel_name]; + try { + this.notebook.start_session(kernel_name); + } catch (e) { + if (e.name === 'SessionAlreadyStarting') { + console.log("Cannot change kernel while waiting for pending session start."); + } else { + // unhandled error + throw e; + } + // only trigger spec_changed if change was successful + return; + } this.events.trigger('spec_changed.Kernel', ks); - this.notebook.session.delete(); - this.notebook.start_session(kernel_name); }; KernelSelector.prototype.bind_events = function() { diff --git a/IPython/html/static/notebook/js/menubar.js b/IPython/html/static/notebook/js/menubar.js index a6f0b28..67e0d5b 100644 --- a/IPython/html/static/notebook/js/menubar.js +++ b/IPython/html/static/notebook/js/menubar.js @@ -157,12 +157,13 @@ define([ } }); this.element.find('#kill_and_exit').click(function () { - that.notebook.session.delete(); - setTimeout(function(){ + var close_window = function () { // allow closing of new tabs in Chromium, impossible in FF window.open('', '_self', ''); window.close(); - }, 500); + }; + // finish with close on success or failure + that.notebook.session.delete(close_window, close_window); }); // Edit this.element.find('#cut_cell').click(function () { diff --git a/IPython/html/static/notebook/js/notebook.js b/IPython/html/static/notebook/js/notebook.js index 02a4439..64f3fbe 100644 --- a/IPython/html/static/notebook/js/notebook.js +++ b/IPython/html/static/notebook/js/notebook.js @@ -62,6 +62,7 @@ define([ this.save_widget = options.save_widget; this.tooltip = new tooltip.Tooltip(this.events); this.ws_url = options.ws_url; + this._session_starting = false; // default_kernel_name is a temporary measure while we implement proper // kernel selection and delayed start. Do not rely on it. this.default_kernel_name = 'python'; @@ -1525,9 +1526,38 @@ define([ * @method start_session */ Notebook.prototype.start_session = function (kernel_name) { + var that = this; if (kernel_name === undefined) { kernel_name = this.default_kernel_name; } + if (this._session_starting) { + throw new session.SessionAlreadyStarting(); + } + this._session_starting = true; + + if (this.session !== null) { + var s = this.session; + this.session = null; + // need to start the new session in a callback after delete, + // because javascript does not guarantee the ordering of AJAX requests (?!) + s.delete(function () { + // on successful delete, start new session + that._session_starting = false; + that.start_session(kernel_name); + }, function (jqXHR, status, error) { + // log the failed delete, but still create a new session + // 404 just means it was already deleted by someone else, + // but other errors are possible. + utils.log_ajax_error(jqXHR, status, error); + that._session_starting = false; + that.start_session(kernel_name); + } + ); + return; + } + + + this.session = new session.Session({ base_url: this.base_url, ws_url: this.ws_url, @@ -1539,7 +1569,10 @@ define([ kernel_name: kernel_name, notebook: this}); - this.session.start($.proxy(this._session_started, this)); + this.session.start( + $.proxy(this._session_started, this), + $.proxy(this._session_start_failed, this) + ); }; @@ -1548,7 +1581,8 @@ define([ * comm manager to the widget manager * */ - Notebook.prototype._session_started = function(){ + Notebook.prototype._session_started = function (){ + this._session_starting = false; this.kernel = this.session.kernel; var ncells = this.ncells(); for (var i=0; i