Skip to content

Commit 4a403a8

Browse files
minrkyuvipanda
authored andcommitted
Backport PR jupyter#1002: Implement delayed evaluation for the cell executions happening before a kernel is set up
This is an attempt at taking care of jupyter#994. We implement two queues for cell executions. One is at the notebook level, to queue up executions when a kernel does not exist. Another is at the kernel level, to queue up messages when the kernel is not connected. *Edit: We scrapped the notebook-level queue, and now just have a kernel-level message queue for when the kernel isn't connected*
1 parent e913af4 commit 4a403a8

File tree

3 files changed

+92
-16
lines changed

3 files changed

+92
-16
lines changed

notebook/static/notebook/js/codecell.js

+5-8
Original file line numberDiff line numberDiff line change
@@ -305,24 +305,21 @@ define([
305305
* @method execute
306306
*/
307307
CodeCell.prototype.execute = function (stop_on_error) {
308-
if (!this.kernel || !this.kernel.is_connected()) {
309-
console.log("Can't execute, kernel is not connected.");
308+
if (!this.kernel) {
309+
console.log("Can't execute cell since kernel is not set.");
310310
return;
311311
}
312312

313-
this.output_area.clear_output(false, true);
314-
315313
if (stop_on_error === undefined) {
316314
stop_on_error = true;
317315
}
318316

317+
this.output_area.clear_output(false, true);
319318
var old_msg_id = this.last_msg_id;
320-
321319
if (old_msg_id) {
322320
this.kernel.clear_callbacks_for_msg(old_msg_id);
323-
if (old_msg_id) {
324-
delete CodeCell.msg_cells[old_msg_id];
325-
}
321+
delete CodeCell.msg_cells[old_msg_id];
322+
this.last_msg_id = null;
326323
}
327324
if (this.get_text().trim().length === 0) {
328325
// nothing to do

notebook/static/services/kernels/kernel.js

+31-8
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ define([
6363
this._autorestart_attempt = 0;
6464
this._reconnect_attempt = 0;
6565
this.reconnect_limit = 7;
66+
67+
this._pending_messages = [];
6668
};
6769

6870
/**
@@ -409,6 +411,15 @@ define([
409411
* @function _kernel_connected
410412
*/
411413
this.events.trigger('kernel_connected.Kernel', {kernel: this});
414+
415+
// Send pending messages. We shift the message off the queue
416+
// after the message is sent so that if there is an exception,
417+
// the message is still pending.
418+
while (this._pending_messages.length > 0) {
419+
this.ws.send(this._pending_messages[0]);
420+
this._pending_messages.shift();
421+
}
422+
412423
// get kernel info so we know what state the kernel is in
413424
var that = this;
414425
this.kernel_info(function (reply) {
@@ -602,18 +613,33 @@ define([
602613
return (this.ws === null);
603614
};
604615

616+
Kernel.prototype._send = function(msg) {
617+
/**
618+
* Send a message (if the kernel is connected) or queue the message for future delivery
619+
*
620+
* Pending messages will automatically be sent when a kernel becomes connected.
621+
*
622+
* @function _send
623+
* @param msg
624+
*/
625+
if (this.is_connected()) {
626+
this.ws.send(msg);
627+
} else {
628+
this._pending_messages.push(msg);
629+
}
630+
}
631+
605632
Kernel.prototype.send_shell_message = function (msg_type, content, callbacks, metadata, buffers) {
606633
/**
607634
* Send a message on the Kernel's shell channel
608635
*
636+
* If the kernel is not connected, the message will be buffered.
637+
*
609638
* @function send_shell_message
610639
*/
611-
if (!this.is_connected()) {
612-
throw new Error("kernel is not connected");
613-
}
614640
var msg = this._get_msg(msg_type, content, metadata, buffers);
615641
msg.channel = 'shell';
616-
this.ws.send(serialize.serialize(msg));
642+
this._send(serialize.serialize(msg));
617643
this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
618644
return msg.header.msg_id;
619645
};
@@ -777,16 +803,13 @@ define([
777803
* @function send_input_reply
778804
*/
779805
Kernel.prototype.send_input_reply = function (input) {
780-
if (!this.is_connected()) {
781-
throw new Error("kernel is not connected");
782-
}
783806
var content = {
784807
value : input
785808
};
786809
this.events.trigger('input_reply.Kernel', {kernel: this, content: content});
787810
var msg = this._get_msg("input_reply", content);
788811
msg.channel = 'stdin';
789-
this.ws.send(serialize.serialize(msg));
812+
this._send(serialize.serialize(msg));
790813
return msg.header.msg_id;
791814
};
792815

notebook/tests/notebook/buffering.js

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//
2+
// Test buffering for execution requests.
3+
//
4+
casper.notebook_test(function () {
5+
this.then(function() {
6+
// make sure there are at least three cells for the tests below.
7+
this.append_cell();
8+
this.append_cell();
9+
this.append_cell();
10+
})
11+
12+
this.thenEvaluate(function () {
13+
IPython.notebook.kernel.stop_channels();
14+
var cell = IPython.notebook.get_cell(0);
15+
cell.set_text('a=10; print(a)');
16+
IPython.notebook.execute_cells([0]);
17+
IPython.notebook.kernel.reconnect(1);
18+
});
19+
20+
this.wait_for_output(0);
21+
22+
this.then(function () {
23+
var result = this.get_output_cell(0);
24+
this.test.assertEquals(result.text, '10\n', 'kernels buffer messages if connection is down');
25+
});
26+
27+
this.thenEvaluate(function () {
28+
var cell = IPython.notebook.get_cell(0);
29+
var cellplus = IPython.notebook.get_cell(1);
30+
var cellprint = IPython.notebook.get_cell(2);
31+
cell.set_text('k=1');
32+
cellplus.set_text('k+=1');
33+
cellprint.set_text('k*=2')
34+
35+
IPython.notebook.kernel.stop_channels();
36+
37+
// Repeated execution of cell queued up in the kernel executes
38+
// each execution request in order.
39+
IPython.notebook.execute_cells([0]);
40+
IPython.notebook.execute_cells([2]);
41+
IPython.notebook.execute_cells([1]);
42+
IPython.notebook.execute_cells([1]);
43+
IPython.notebook.execute_cells([1]);
44+
cellprint.set_text('print(k)')
45+
IPython.notebook.execute_cells([2]);
46+
47+
IPython.notebook.kernel.reconnect(1);
48+
});
49+
50+
this.wait_for_output(2);
51+
52+
this.then(function () {
53+
var result = this.get_output_cell(2);
54+
this.test.assertEquals(result.text, '5\n', 'kernels send buffered messages in order');
55+
});
56+
});

0 commit comments

Comments
 (0)