##// END OF EJS Templates
Handle kernel messages synchronously...
Jason Grout -
Show More
@@ -1,217 +1,219 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 define([
4 define([
5 'base/js/namespace',
5 'base/js/namespace',
6 'jquery',
6 'jquery',
7 'base/js/utils',
7 'base/js/utils',
8 ], function(IPython, $, utils) {
8 ], function(IPython, $, utils) {
9 "use strict";
9 "use strict";
10
10
11 //-----------------------------------------------------------------------
11 //-----------------------------------------------------------------------
12 // CommManager class
12 // CommManager class
13 //-----------------------------------------------------------------------
13 //-----------------------------------------------------------------------
14
14
15 var CommManager = function (kernel) {
15 var CommManager = function (kernel) {
16 this.comms = {};
16 this.comms = {};
17 this.targets = {};
17 this.targets = {};
18 if (kernel !== undefined) {
18 if (kernel !== undefined) {
19 this.init_kernel(kernel);
19 this.init_kernel(kernel);
20 }
20 }
21 };
21 };
22
22
23 CommManager.prototype.init_kernel = function (kernel) {
23 CommManager.prototype.init_kernel = function (kernel) {
24 /**
24 /**
25 * connect the kernel, and register message handlers
25 * connect the kernel, and register message handlers
26 */
26 */
27 this.kernel = kernel;
27 this.kernel = kernel;
28 var msg_types = ['comm_open', 'comm_msg', 'comm_close'];
28 var msg_types = ['comm_open', 'comm_msg', 'comm_close'];
29 for (var i = 0; i < msg_types.length; i++) {
29 for (var i = 0; i < msg_types.length; i++) {
30 var msg_type = msg_types[i];
30 var msg_type = msg_types[i];
31 kernel.register_iopub_handler(msg_type, $.proxy(this[msg_type], this));
31 kernel.register_iopub_handler(msg_type, $.proxy(this[msg_type], this));
32 }
32 }
33 };
33 };
34
34
35 CommManager.prototype.new_comm = function (target_name, data, callbacks, metadata) {
35 CommManager.prototype.new_comm = function (target_name, data, callbacks, metadata) {
36 /**
36 /**
37 * Create a new Comm, register it, and open its Kernel-side counterpart
37 * Create a new Comm, register it, and open its Kernel-side counterpart
38 * Mimics the auto-registration in `Comm.__init__` in the IPython Comm
38 * Mimics the auto-registration in `Comm.__init__` in the IPython Comm
39 */
39 */
40 var comm = new Comm(target_name);
40 var comm = new Comm(target_name);
41 this.register_comm(comm);
41 this.register_comm(comm);
42 comm.open(data, callbacks, metadata);
42 comm.open(data, callbacks, metadata);
43 return comm;
43 return comm;
44 };
44 };
45
45
46 CommManager.prototype.register_target = function (target_name, f) {
46 CommManager.prototype.register_target = function (target_name, f) {
47 /**
47 /**
48 * Register a target function for a given target name
48 * Register a target function for a given target name
49 */
49 */
50 this.targets[target_name] = f;
50 this.targets[target_name] = f;
51 };
51 };
52
52
53 CommManager.prototype.unregister_target = function (target_name, f) {
53 CommManager.prototype.unregister_target = function (target_name, f) {
54 /**
54 /**
55 * Unregister a target function for a given target name
55 * Unregister a target function for a given target name
56 */
56 */
57 delete this.targets[target_name];
57 delete this.targets[target_name];
58 };
58 };
59
59
60 CommManager.prototype.register_comm = function (comm) {
60 CommManager.prototype.register_comm = function (comm) {
61 /**
61 /**
62 * Register a comm in the mapping
62 * Register a comm in the mapping
63 */
63 */
64 this.comms[comm.comm_id] = Promise.resolve(comm);
64 this.comms[comm.comm_id] = Promise.resolve(comm);
65 comm.kernel = this.kernel;
65 comm.kernel = this.kernel;
66 return comm.comm_id;
66 return comm.comm_id;
67 };
67 };
68
68
69 CommManager.prototype.unregister_comm = function (comm) {
69 CommManager.prototype.unregister_comm = function (comm) {
70 /**
70 /**
71 * Remove a comm from the mapping
71 * Remove a comm from the mapping
72 */
72 */
73 delete this.comms[comm.comm_id];
73 delete this.comms[comm.comm_id];
74 };
74 };
75
75
76 // comm message handlers
76 // comm message handlers
77
77
78 CommManager.prototype.comm_open = function (msg) {
78 CommManager.prototype.comm_open = function (msg) {
79 var content = msg.content;
79 var content = msg.content;
80 var that = this;
80 var that = this;
81 var comm_id = content.comm_id;
81 var comm_id = content.comm_id;
82
82
83 this.comms[comm_id] = utils.load_class(content.target_name, content.target_module,
83 this.comms[comm_id] = utils.load_class(content.target_name, content.target_module,
84 this.targets).then(function(target) {
84 this.targets).then(function(target) {
85 var comm = new Comm(content.target_name, comm_id);
85 var comm = new Comm(content.target_name, comm_id);
86 comm.kernel = that.kernel;
86 comm.kernel = that.kernel;
87 try {
87 try {
88 var response = target(comm, msg);
88 var response = target(comm, msg);
89 } catch (e) {
89 } catch (e) {
90 comm.close();
90 comm.close();
91 that.unregister_comm(comm);
91 that.unregister_comm(comm);
92 var wrapped_error = new utils.WrappedError("Exception opening new comm", e);
92 var wrapped_error = new utils.WrappedError("Exception opening new comm", e);
93 console.error(wrapped_error);
93 console.error(wrapped_error);
94 return Promise.reject(wrapped_error);
94 return Promise.reject(wrapped_error);
95 }
95 }
96 // Regardless of the target return value, we need to
96 // Regardless of the target return value, we need to
97 // then return the comm
97 // then return the comm
98 return Promise.resolve(response).then(function() {return comm;});
98 return Promise.resolve(response).then(function() {return comm;});
99 }, utils.reject('Could not open comm', true));
99 }, utils.reject('Could not open comm', true));
100 return this.comms[comm_id];
100 return this.comms[comm_id];
101 };
101 };
102
102
103 CommManager.prototype.comm_close = function(msg) {
103 CommManager.prototype.comm_close = function(msg) {
104 var content = msg.content;
104 var content = msg.content;
105 if (this.comms[content.comm_id] === undefined) {
105 if (this.comms[content.comm_id] === undefined) {
106 console.error('Comm promise not found for comm id ' + content.comm_id);
106 console.error('Comm promise not found for comm id ' + content.comm_id);
107 return;
107 return;
108 }
108 }
109 var that = this;
109 var that = this;
110 this.comms[content.comm_id] = this.comms[content.comm_id].then(function(comm) {
110 this.comms[content.comm_id] = this.comms[content.comm_id].then(function(comm) {
111 that.unregister_comm(comm);
111 that.unregister_comm(comm);
112 try {
112 try {
113 comm.handle_close(msg);
113 comm.handle_close(msg);
114 } catch (e) {
114 } catch (e) {
115 console.log("Exception closing comm: ", e, e.stack, msg);
115 console.log("Exception closing comm: ", e, e.stack, msg);
116 }
116 }
117 // don't return a comm, so that further .then() functions
117 // don't return a comm, so that further .then() functions
118 // get an undefined comm input
118 // get an undefined comm input
119 });
119 });
120 return this.comms[content.comm_id];
120 };
121 };
121
122
122 CommManager.prototype.comm_msg = function(msg) {
123 CommManager.prototype.comm_msg = function(msg) {
123 var content = msg.content;
124 var content = msg.content;
124 if (this.comms[content.comm_id] === undefined) {
125 if (this.comms[content.comm_id] === undefined) {
125 console.error('Comm promise not found for comm id ' + content.comm_id);
126 console.error('Comm promise not found for comm id ' + content.comm_id);
126 return;
127 return;
127 }
128 }
128
129
129 this.comms[content.comm_id] = this.comms[content.comm_id].then(function(comm) {
130 this.comms[content.comm_id] = this.comms[content.comm_id].then(function(comm) {
130 try {
131 try {
131 comm.handle_msg(msg);
132 comm.handle_msg(msg);
132 } catch (e) {
133 } catch (e) {
133 console.log("Exception handling comm msg: ", e, e.stack, msg);
134 console.log("Exception handling comm msg: ", e, e.stack, msg);
134 }
135 }
135 return comm;
136 return comm;
136 });
137 });
138 return this.comms[content.comm_id];
137 };
139 };
138
140
139 //-----------------------------------------------------------------------
141 //-----------------------------------------------------------------------
140 // Comm base class
142 // Comm base class
141 //-----------------------------------------------------------------------
143 //-----------------------------------------------------------------------
142
144
143 var Comm = function (target_name, comm_id) {
145 var Comm = function (target_name, comm_id) {
144 this.target_name = target_name;
146 this.target_name = target_name;
145 this.comm_id = comm_id || utils.uuid();
147 this.comm_id = comm_id || utils.uuid();
146 this._msg_callback = this._close_callback = null;
148 this._msg_callback = this._close_callback = null;
147 };
149 };
148
150
149 // methods for sending messages
151 // methods for sending messages
150 Comm.prototype.open = function (data, callbacks, metadata) {
152 Comm.prototype.open = function (data, callbacks, metadata) {
151 var content = {
153 var content = {
152 comm_id : this.comm_id,
154 comm_id : this.comm_id,
153 target_name : this.target_name,
155 target_name : this.target_name,
154 data : data || {},
156 data : data || {},
155 };
157 };
156 return this.kernel.send_shell_message("comm_open", content, callbacks, metadata);
158 return this.kernel.send_shell_message("comm_open", content, callbacks, metadata);
157 };
159 };
158
160
159 Comm.prototype.send = function (data, callbacks, metadata, buffers) {
161 Comm.prototype.send = function (data, callbacks, metadata, buffers) {
160 var content = {
162 var content = {
161 comm_id : this.comm_id,
163 comm_id : this.comm_id,
162 data : data || {},
164 data : data || {},
163 };
165 };
164 return this.kernel.send_shell_message("comm_msg", content, callbacks, metadata, buffers);
166 return this.kernel.send_shell_message("comm_msg", content, callbacks, metadata, buffers);
165 };
167 };
166
168
167 Comm.prototype.close = function (data, callbacks, metadata) {
169 Comm.prototype.close = function (data, callbacks, metadata) {
168 var content = {
170 var content = {
169 comm_id : this.comm_id,
171 comm_id : this.comm_id,
170 data : data || {},
172 data : data || {},
171 };
173 };
172 return this.kernel.send_shell_message("comm_close", content, callbacks, metadata);
174 return this.kernel.send_shell_message("comm_close", content, callbacks, metadata);
173 };
175 };
174
176
175 // methods for registering callbacks for incoming messages
177 // methods for registering callbacks for incoming messages
176 Comm.prototype._register_callback = function (key, callback) {
178 Comm.prototype._register_callback = function (key, callback) {
177 this['_' + key + '_callback'] = callback;
179 this['_' + key + '_callback'] = callback;
178 };
180 };
179
181
180 Comm.prototype.on_msg = function (callback) {
182 Comm.prototype.on_msg = function (callback) {
181 this._register_callback('msg', callback);
183 this._register_callback('msg', callback);
182 };
184 };
183
185
184 Comm.prototype.on_close = function (callback) {
186 Comm.prototype.on_close = function (callback) {
185 this._register_callback('close', callback);
187 this._register_callback('close', callback);
186 };
188 };
187
189
188 // methods for handling incoming messages
190 // methods for handling incoming messages
189
191
190 Comm.prototype._callback = function (key, msg) {
192 Comm.prototype._callback = function (key, msg) {
191 var callback = this['_' + key + '_callback'];
193 var callback = this['_' + key + '_callback'];
192 if (callback) {
194 if (callback) {
193 try {
195 try {
194 callback(msg);
196 callback(msg);
195 } catch (e) {
197 } catch (e) {
196 console.log("Exception in Comm callback", e, e.stack, msg);
198 console.log("Exception in Comm callback", e, e.stack, msg);
197 }
199 }
198 }
200 }
199 };
201 };
200
202
201 Comm.prototype.handle_msg = function (msg) {
203 Comm.prototype.handle_msg = function (msg) {
202 this._callback('msg', msg);
204 this._callback('msg', msg);
203 };
205 };
204
206
205 Comm.prototype.handle_close = function (msg) {
207 Comm.prototype.handle_close = function (msg) {
206 this._callback('close', msg);
208 this._callback('close', msg);
207 };
209 };
208
210
209 // For backwards compatability.
211 // For backwards compatability.
210 IPython.CommManager = CommManager;
212 IPython.CommManager = CommManager;
211 IPython.Comm = Comm;
213 IPython.Comm = Comm;
212
214
213 return {
215 return {
214 'CommManager': CommManager,
216 'CommManager': CommManager,
215 'Comm': Comm
217 'Comm': Comm
216 };
218 };
217 });
219 });
@@ -1,1056 +1,1064 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 define([
4 define([
5 'base/js/namespace',
5 'base/js/namespace',
6 'jquery',
6 'jquery',
7 'base/js/utils',
7 'base/js/utils',
8 './comm',
8 './comm',
9 './serialize',
9 './serialize',
10 'widgets/js/init'
10 'widgets/js/init'
11 ], function(IPython, $, utils, comm, serialize, widgetmanager) {
11 ], function(IPython, $, utils, comm, serialize, widgetmanager) {
12 "use strict";
12 "use strict";
13
13
14 /**
14 /**
15 * A Kernel class to communicate with the Python kernel. This
15 * A Kernel class to communicate with the Python kernel. This
16 * should generally not be constructed directly, but be created
16 * should generally not be constructed directly, but be created
17 * by. the `Session` object. Once created, this object should be
17 * by. the `Session` object. Once created, this object should be
18 * used to communicate with the kernel.
18 * used to communicate with the kernel.
19 *
19 *
20 * @class Kernel
20 * @class Kernel
21 * @param {string} kernel_service_url - the URL to access the kernel REST api
21 * @param {string} kernel_service_url - the URL to access the kernel REST api
22 * @param {string} ws_url - the websockets URL
22 * @param {string} ws_url - the websockets URL
23 * @param {Notebook} notebook - notebook object
23 * @param {Notebook} notebook - notebook object
24 * @param {string} name - the kernel type (e.g. python3)
24 * @param {string} name - the kernel type (e.g. python3)
25 */
25 */
26 var Kernel = function (kernel_service_url, ws_url, notebook, name) {
26 var Kernel = function (kernel_service_url, ws_url, notebook, name) {
27 this.events = notebook.events;
27 this.events = notebook.events;
28
28
29 this.id = null;
29 this.id = null;
30 this.name = name;
30 this.name = name;
31 this.ws = null;
31 this.ws = null;
32
32
33 this.kernel_service_url = kernel_service_url;
33 this.kernel_service_url = kernel_service_url;
34 this.kernel_url = null;
34 this.kernel_url = null;
35 this.ws_url = ws_url || IPython.utils.get_body_data("wsUrl");
35 this.ws_url = ws_url || IPython.utils.get_body_data("wsUrl");
36 if (!this.ws_url) {
36 if (!this.ws_url) {
37 // trailing 's' in https will become wss for secure web sockets
37 // trailing 's' in https will become wss for secure web sockets
38 this.ws_url = location.protocol.replace('http', 'ws') + "//" + location.host;
38 this.ws_url = location.protocol.replace('http', 'ws') + "//" + location.host;
39 }
39 }
40
40
41 this.username = "username";
41 this.username = "username";
42 this.session_id = utils.uuid();
42 this.session_id = utils.uuid();
43 this._msg_callbacks = {};
43 this._msg_callbacks = {};
44 this._msg_queue = Promise.resolve();
44 this._msg_queue = Promise.resolve();
45 this.info_reply = {}; // kernel_info_reply stored here after starting
45 this.info_reply = {}; // kernel_info_reply stored here after starting
46
46
47 if (typeof(WebSocket) !== 'undefined') {
47 if (typeof(WebSocket) !== 'undefined') {
48 this.WebSocket = WebSocket;
48 this.WebSocket = WebSocket;
49 } else if (typeof(MozWebSocket) !== 'undefined') {
49 } else if (typeof(MozWebSocket) !== 'undefined') {
50 this.WebSocket = MozWebSocket;
50 this.WebSocket = MozWebSocket;
51 } else {
51 } else {
52 alert('Your browser does not have WebSocket support, please try Chrome, Safari or Firefox β‰₯ 6. Firefox 4 and 5 are also supported by you have to enable WebSockets in about:config.');
52 alert('Your browser does not have WebSocket support, please try Chrome, Safari or Firefox β‰₯ 6. Firefox 4 and 5 are also supported by you have to enable WebSockets in about:config.');
53 }
53 }
54
54
55 this.bind_events();
55 this.bind_events();
56 this.init_iopub_handlers();
56 this.init_iopub_handlers();
57 this.comm_manager = new comm.CommManager(this);
57 this.comm_manager = new comm.CommManager(this);
58 this.widget_manager = new widgetmanager.WidgetManager(this.comm_manager, notebook);
58 this.widget_manager = new widgetmanager.WidgetManager(this.comm_manager, notebook);
59
59
60 this.last_msg_id = null;
60 this.last_msg_id = null;
61 this.last_msg_callbacks = {};
61 this.last_msg_callbacks = {};
62
62
63 this._autorestart_attempt = 0;
63 this._autorestart_attempt = 0;
64 this._reconnect_attempt = 0;
64 this._reconnect_attempt = 0;
65 this.reconnect_limit = 7;
65 this.reconnect_limit = 7;
66 };
66 };
67
67
68 /**
68 /**
69 * @function _get_msg
69 * @function _get_msg
70 */
70 */
71 Kernel.prototype._get_msg = function (msg_type, content, metadata, buffers) {
71 Kernel.prototype._get_msg = function (msg_type, content, metadata, buffers) {
72 var msg = {
72 var msg = {
73 header : {
73 header : {
74 msg_id : utils.uuid(),
74 msg_id : utils.uuid(),
75 username : this.username,
75 username : this.username,
76 session : this.session_id,
76 session : this.session_id,
77 msg_type : msg_type,
77 msg_type : msg_type,
78 version : "5.0"
78 version : "5.0"
79 },
79 },
80 metadata : metadata || {},
80 metadata : metadata || {},
81 content : content,
81 content : content,
82 buffers : buffers || [],
82 buffers : buffers || [],
83 parent_header : {}
83 parent_header : {}
84 };
84 };
85 return msg;
85 return msg;
86 };
86 };
87
87
88 /**
88 /**
89 * @function bind_events
89 * @function bind_events
90 */
90 */
91 Kernel.prototype.bind_events = function () {
91 Kernel.prototype.bind_events = function () {
92 var that = this;
92 var that = this;
93 this.events.on('send_input_reply.Kernel', function(evt, data) {
93 this.events.on('send_input_reply.Kernel', function(evt, data) {
94 that.send_input_reply(data);
94 that.send_input_reply(data);
95 });
95 });
96
96
97 var record_status = function (evt, info) {
97 var record_status = function (evt, info) {
98 console.log('Kernel: ' + evt.type + ' (' + info.kernel.id + ')');
98 console.log('Kernel: ' + evt.type + ' (' + info.kernel.id + ')');
99 };
99 };
100
100
101 this.events.on('kernel_created.Kernel', record_status);
101 this.events.on('kernel_created.Kernel', record_status);
102 this.events.on('kernel_reconnecting.Kernel', record_status);
102 this.events.on('kernel_reconnecting.Kernel', record_status);
103 this.events.on('kernel_connected.Kernel', record_status);
103 this.events.on('kernel_connected.Kernel', record_status);
104 this.events.on('kernel_starting.Kernel', record_status);
104 this.events.on('kernel_starting.Kernel', record_status);
105 this.events.on('kernel_restarting.Kernel', record_status);
105 this.events.on('kernel_restarting.Kernel', record_status);
106 this.events.on('kernel_autorestarting.Kernel', record_status);
106 this.events.on('kernel_autorestarting.Kernel', record_status);
107 this.events.on('kernel_interrupting.Kernel', record_status);
107 this.events.on('kernel_interrupting.Kernel', record_status);
108 this.events.on('kernel_disconnected.Kernel', record_status);
108 this.events.on('kernel_disconnected.Kernel', record_status);
109 // these are commented out because they are triggered a lot, but can
109 // these are commented out because they are triggered a lot, but can
110 // be uncommented for debugging purposes
110 // be uncommented for debugging purposes
111 //this.events.on('kernel_idle.Kernel', record_status);
111 //this.events.on('kernel_idle.Kernel', record_status);
112 //this.events.on('kernel_busy.Kernel', record_status);
112 //this.events.on('kernel_busy.Kernel', record_status);
113 this.events.on('kernel_ready.Kernel', record_status);
113 this.events.on('kernel_ready.Kernel', record_status);
114 this.events.on('kernel_killed.Kernel', record_status);
114 this.events.on('kernel_killed.Kernel', record_status);
115 this.events.on('kernel_dead.Kernel', record_status);
115 this.events.on('kernel_dead.Kernel', record_status);
116
116
117 this.events.on('kernel_ready.Kernel', function () {
117 this.events.on('kernel_ready.Kernel', function () {
118 that._autorestart_attempt = 0;
118 that._autorestart_attempt = 0;
119 });
119 });
120 this.events.on('kernel_connected.Kernel', function () {
120 this.events.on('kernel_connected.Kernel', function () {
121 that._reconnect_attempt = 0;
121 that._reconnect_attempt = 0;
122 });
122 });
123 };
123 };
124
124
125 /**
125 /**
126 * Initialize the iopub handlers.
126 * Initialize the iopub handlers.
127 *
127 *
128 * @function init_iopub_handlers
128 * @function init_iopub_handlers
129 */
129 */
130 Kernel.prototype.init_iopub_handlers = function () {
130 Kernel.prototype.init_iopub_handlers = function () {
131 var output_msg_types = ['stream', 'display_data', 'execute_result', 'error'];
131 var output_msg_types = ['stream', 'display_data', 'execute_result', 'error'];
132 this._iopub_handlers = {};
132 this._iopub_handlers = {};
133 this.register_iopub_handler('status', $.proxy(this._handle_status_message, this));
133 this.register_iopub_handler('status', $.proxy(this._handle_status_message, this));
134 this.register_iopub_handler('clear_output', $.proxy(this._handle_clear_output, this));
134 this.register_iopub_handler('clear_output', $.proxy(this._handle_clear_output, this));
135 this.register_iopub_handler('execute_input', $.proxy(this._handle_input_message, this));
135 this.register_iopub_handler('execute_input', $.proxy(this._handle_input_message, this));
136
136
137 for (var i=0; i < output_msg_types.length; i++) {
137 for (var i=0; i < output_msg_types.length; i++) {
138 this.register_iopub_handler(output_msg_types[i], $.proxy(this._handle_output_message, this));
138 this.register_iopub_handler(output_msg_types[i], $.proxy(this._handle_output_message, this));
139 }
139 }
140 };
140 };
141
141
142 /**
142 /**
143 * GET /api/kernels
143 * GET /api/kernels
144 *
144 *
145 * Get the list of running kernels.
145 * Get the list of running kernels.
146 *
146 *
147 * @function list
147 * @function list
148 * @param {function} [success] - function executed on ajax success
148 * @param {function} [success] - function executed on ajax success
149 * @param {function} [error] - functon executed on ajax error
149 * @param {function} [error] - functon executed on ajax error
150 */
150 */
151 Kernel.prototype.list = function (success, error) {
151 Kernel.prototype.list = function (success, error) {
152 $.ajax(this.kernel_service_url, {
152 $.ajax(this.kernel_service_url, {
153 processData: false,
153 processData: false,
154 cache: false,
154 cache: false,
155 type: "GET",
155 type: "GET",
156 dataType: "json",
156 dataType: "json",
157 success: success,
157 success: success,
158 error: this._on_error(error)
158 error: this._on_error(error)
159 });
159 });
160 };
160 };
161
161
162 /**
162 /**
163 * POST /api/kernels
163 * POST /api/kernels
164 *
164 *
165 * Start a new kernel.
165 * Start a new kernel.
166 *
166 *
167 * In general this shouldn't be used -- the kernel should be
167 * In general this shouldn't be used -- the kernel should be
168 * started through the session API. If you use this function and
168 * started through the session API. If you use this function and
169 * are also using the session API then your session and kernel
169 * are also using the session API then your session and kernel
170 * WILL be out of sync!
170 * WILL be out of sync!
171 *
171 *
172 * @function start
172 * @function start
173 * @param {params} [Object] - parameters to include in the query string
173 * @param {params} [Object] - parameters to include in the query string
174 * @param {function} [success] - function executed on ajax success
174 * @param {function} [success] - function executed on ajax success
175 * @param {function} [error] - functon executed on ajax error
175 * @param {function} [error] - functon executed on ajax error
176 */
176 */
177 Kernel.prototype.start = function (params, success, error) {
177 Kernel.prototype.start = function (params, success, error) {
178 var url = this.kernel_service_url;
178 var url = this.kernel_service_url;
179 var qs = $.param(params || {}); // query string for sage math stuff
179 var qs = $.param(params || {}); // query string for sage math stuff
180 if (qs !== "") {
180 if (qs !== "") {
181 url = url + "?" + qs;
181 url = url + "?" + qs;
182 }
182 }
183
183
184 this.events.trigger('kernel_starting.Kernel', {kernel: this});
184 this.events.trigger('kernel_starting.Kernel', {kernel: this});
185 var that = this;
185 var that = this;
186 var on_success = function (data, status, xhr) {
186 var on_success = function (data, status, xhr) {
187 that.events.trigger('kernel_created.Kernel', {kernel: that});
187 that.events.trigger('kernel_created.Kernel', {kernel: that});
188 that._kernel_created(data);
188 that._kernel_created(data);
189 if (success) {
189 if (success) {
190 success(data, status, xhr);
190 success(data, status, xhr);
191 }
191 }
192 };
192 };
193
193
194 $.ajax(url, {
194 $.ajax(url, {
195 processData: false,
195 processData: false,
196 cache: false,
196 cache: false,
197 type: "POST",
197 type: "POST",
198 data: JSON.stringify({name: this.name}),
198 data: JSON.stringify({name: this.name}),
199 dataType: "json",
199 dataType: "json",
200 success: this._on_success(on_success),
200 success: this._on_success(on_success),
201 error: this._on_error(error)
201 error: this._on_error(error)
202 });
202 });
203
203
204 return url;
204 return url;
205 };
205 };
206
206
207 /**
207 /**
208 * GET /api/kernels/[:kernel_id]
208 * GET /api/kernels/[:kernel_id]
209 *
209 *
210 * Get information about the kernel.
210 * Get information about the kernel.
211 *
211 *
212 * @function get_info
212 * @function get_info
213 * @param {function} [success] - function executed on ajax success
213 * @param {function} [success] - function executed on ajax success
214 * @param {function} [error] - functon executed on ajax error
214 * @param {function} [error] - functon executed on ajax error
215 */
215 */
216 Kernel.prototype.get_info = function (success, error) {
216 Kernel.prototype.get_info = function (success, error) {
217 $.ajax(this.kernel_url, {
217 $.ajax(this.kernel_url, {
218 processData: false,
218 processData: false,
219 cache: false,
219 cache: false,
220 type: "GET",
220 type: "GET",
221 dataType: "json",
221 dataType: "json",
222 success: this._on_success(success),
222 success: this._on_success(success),
223 error: this._on_error(error)
223 error: this._on_error(error)
224 });
224 });
225 };
225 };
226
226
227 /**
227 /**
228 * DELETE /api/kernels/[:kernel_id]
228 * DELETE /api/kernels/[:kernel_id]
229 *
229 *
230 * Shutdown the kernel.
230 * Shutdown the kernel.
231 *
231 *
232 * If you are also using sessions, then this function shoul NOT be
232 * If you are also using sessions, then this function shoul NOT be
233 * used. Instead, use Session.delete. Otherwise, the session and
233 * used. Instead, use Session.delete. Otherwise, the session and
234 * kernel WILL be out of sync.
234 * kernel WILL be out of sync.
235 *
235 *
236 * @function kill
236 * @function kill
237 * @param {function} [success] - function executed on ajax success
237 * @param {function} [success] - function executed on ajax success
238 * @param {function} [error] - functon executed on ajax error
238 * @param {function} [error] - functon executed on ajax error
239 */
239 */
240 Kernel.prototype.kill = function (success, error) {
240 Kernel.prototype.kill = function (success, error) {
241 this.events.trigger('kernel_killed.Kernel', {kernel: this});
241 this.events.trigger('kernel_killed.Kernel', {kernel: this});
242 this._kernel_dead();
242 this._kernel_dead();
243 $.ajax(this.kernel_url, {
243 $.ajax(this.kernel_url, {
244 processData: false,
244 processData: false,
245 cache: false,
245 cache: false,
246 type: "DELETE",
246 type: "DELETE",
247 dataType: "json",
247 dataType: "json",
248 success: this._on_success(success),
248 success: this._on_success(success),
249 error: this._on_error(error)
249 error: this._on_error(error)
250 });
250 });
251 };
251 };
252
252
253 /**
253 /**
254 * POST /api/kernels/[:kernel_id]/interrupt
254 * POST /api/kernels/[:kernel_id]/interrupt
255 *
255 *
256 * Interrupt the kernel.
256 * Interrupt the kernel.
257 *
257 *
258 * @function interrupt
258 * @function interrupt
259 * @param {function} [success] - function executed on ajax success
259 * @param {function} [success] - function executed on ajax success
260 * @param {function} [error] - functon executed on ajax error
260 * @param {function} [error] - functon executed on ajax error
261 */
261 */
262 Kernel.prototype.interrupt = function (success, error) {
262 Kernel.prototype.interrupt = function (success, error) {
263 this.events.trigger('kernel_interrupting.Kernel', {kernel: this});
263 this.events.trigger('kernel_interrupting.Kernel', {kernel: this});
264
264
265 var that = this;
265 var that = this;
266 var on_success = function (data, status, xhr) {
266 var on_success = function (data, status, xhr) {
267 /**
267 /**
268 * get kernel info so we know what state the kernel is in
268 * get kernel info so we know what state the kernel is in
269 */
269 */
270 that.kernel_info();
270 that.kernel_info();
271 if (success) {
271 if (success) {
272 success(data, status, xhr);
272 success(data, status, xhr);
273 }
273 }
274 };
274 };
275
275
276 var url = utils.url_join_encode(this.kernel_url, 'interrupt');
276 var url = utils.url_join_encode(this.kernel_url, 'interrupt');
277 $.ajax(url, {
277 $.ajax(url, {
278 processData: false,
278 processData: false,
279 cache: false,
279 cache: false,
280 type: "POST",
280 type: "POST",
281 dataType: "json",
281 dataType: "json",
282 success: this._on_success(on_success),
282 success: this._on_success(on_success),
283 error: this._on_error(error)
283 error: this._on_error(error)
284 });
284 });
285 };
285 };
286
286
287 Kernel.prototype.restart = function (success, error) {
287 Kernel.prototype.restart = function (success, error) {
288 /**
288 /**
289 * POST /api/kernels/[:kernel_id]/restart
289 * POST /api/kernels/[:kernel_id]/restart
290 *
290 *
291 * Restart the kernel.
291 * Restart the kernel.
292 *
292 *
293 * @function interrupt
293 * @function interrupt
294 * @param {function} [success] - function executed on ajax success
294 * @param {function} [success] - function executed on ajax success
295 * @param {function} [error] - functon executed on ajax error
295 * @param {function} [error] - functon executed on ajax error
296 */
296 */
297 this.events.trigger('kernel_restarting.Kernel', {kernel: this});
297 this.events.trigger('kernel_restarting.Kernel', {kernel: this});
298 this.stop_channels();
298 this.stop_channels();
299
299
300 var that = this;
300 var that = this;
301 var on_success = function (data, status, xhr) {
301 var on_success = function (data, status, xhr) {
302 that.events.trigger('kernel_created.Kernel', {kernel: that});
302 that.events.trigger('kernel_created.Kernel', {kernel: that});
303 that._kernel_created(data);
303 that._kernel_created(data);
304 if (success) {
304 if (success) {
305 success(data, status, xhr);
305 success(data, status, xhr);
306 }
306 }
307 };
307 };
308
308
309 var on_error = function (xhr, status, err) {
309 var on_error = function (xhr, status, err) {
310 that.events.trigger('kernel_dead.Kernel', {kernel: that});
310 that.events.trigger('kernel_dead.Kernel', {kernel: that});
311 that._kernel_dead();
311 that._kernel_dead();
312 if (error) {
312 if (error) {
313 error(xhr, status, err);
313 error(xhr, status, err);
314 }
314 }
315 };
315 };
316
316
317 var url = utils.url_join_encode(this.kernel_url, 'restart');
317 var url = utils.url_join_encode(this.kernel_url, 'restart');
318 $.ajax(url, {
318 $.ajax(url, {
319 processData: false,
319 processData: false,
320 cache: false,
320 cache: false,
321 type: "POST",
321 type: "POST",
322 dataType: "json",
322 dataType: "json",
323 success: this._on_success(on_success),
323 success: this._on_success(on_success),
324 error: this._on_error(on_error)
324 error: this._on_error(on_error)
325 });
325 });
326 };
326 };
327
327
328 Kernel.prototype.reconnect = function () {
328 Kernel.prototype.reconnect = function () {
329 /**
329 /**
330 * Reconnect to a disconnected kernel. This is not actually a
330 * Reconnect to a disconnected kernel. This is not actually a
331 * standard HTTP request, but useful function nonetheless for
331 * standard HTTP request, but useful function nonetheless for
332 * reconnecting to the kernel if the connection is somehow lost.
332 * reconnecting to the kernel if the connection is somehow lost.
333 *
333 *
334 * @function reconnect
334 * @function reconnect
335 */
335 */
336 if (this.is_connected()) {
336 if (this.is_connected()) {
337 return;
337 return;
338 }
338 }
339 this._reconnect_attempt = this._reconnect_attempt + 1;
339 this._reconnect_attempt = this._reconnect_attempt + 1;
340 this.events.trigger('kernel_reconnecting.Kernel', {
340 this.events.trigger('kernel_reconnecting.Kernel', {
341 kernel: this,
341 kernel: this,
342 attempt: this._reconnect_attempt,
342 attempt: this._reconnect_attempt,
343 });
343 });
344 this.start_channels();
344 this.start_channels();
345 };
345 };
346
346
347 Kernel.prototype._on_success = function (success) {
347 Kernel.prototype._on_success = function (success) {
348 /**
348 /**
349 * Handle a successful AJAX request by updating the kernel id and
349 * Handle a successful AJAX request by updating the kernel id and
350 * name from the response, and then optionally calling a provided
350 * name from the response, and then optionally calling a provided
351 * callback.
351 * callback.
352 *
352 *
353 * @function _on_success
353 * @function _on_success
354 * @param {function} success - callback
354 * @param {function} success - callback
355 */
355 */
356 var that = this;
356 var that = this;
357 return function (data, status, xhr) {
357 return function (data, status, xhr) {
358 if (data) {
358 if (data) {
359 that.id = data.id;
359 that.id = data.id;
360 that.name = data.name;
360 that.name = data.name;
361 }
361 }
362 that.kernel_url = utils.url_join_encode(that.kernel_service_url, that.id);
362 that.kernel_url = utils.url_join_encode(that.kernel_service_url, that.id);
363 if (success) {
363 if (success) {
364 success(data, status, xhr);
364 success(data, status, xhr);
365 }
365 }
366 };
366 };
367 };
367 };
368
368
369 Kernel.prototype._on_error = function (error) {
369 Kernel.prototype._on_error = function (error) {
370 /**
370 /**
371 * Handle a failed AJAX request by logging the error message, and
371 * Handle a failed AJAX request by logging the error message, and
372 * then optionally calling a provided callback.
372 * then optionally calling a provided callback.
373 *
373 *
374 * @function _on_error
374 * @function _on_error
375 * @param {function} error - callback
375 * @param {function} error - callback
376 */
376 */
377 return function (xhr, status, err) {
377 return function (xhr, status, err) {
378 utils.log_ajax_error(xhr, status, err);
378 utils.log_ajax_error(xhr, status, err);
379 if (error) {
379 if (error) {
380 error(xhr, status, err);
380 error(xhr, status, err);
381 }
381 }
382 };
382 };
383 };
383 };
384
384
385 Kernel.prototype._kernel_created = function (data) {
385 Kernel.prototype._kernel_created = function (data) {
386 /**
386 /**
387 * Perform necessary tasks once the kernel has been started,
387 * Perform necessary tasks once the kernel has been started,
388 * including actually connecting to the kernel.
388 * including actually connecting to the kernel.
389 *
389 *
390 * @function _kernel_created
390 * @function _kernel_created
391 * @param {Object} data - information about the kernel including id
391 * @param {Object} data - information about the kernel including id
392 */
392 */
393 this.id = data.id;
393 this.id = data.id;
394 this.kernel_url = utils.url_join_encode(this.kernel_service_url, this.id);
394 this.kernel_url = utils.url_join_encode(this.kernel_service_url, this.id);
395 this.start_channels();
395 this.start_channels();
396 };
396 };
397
397
398 Kernel.prototype._kernel_connected = function () {
398 Kernel.prototype._kernel_connected = function () {
399 /**
399 /**
400 * Perform necessary tasks once the connection to the kernel has
400 * Perform necessary tasks once the connection to the kernel has
401 * been established. This includes requesting information about
401 * been established. This includes requesting information about
402 * the kernel.
402 * the kernel.
403 *
403 *
404 * @function _kernel_connected
404 * @function _kernel_connected
405 */
405 */
406 this.events.trigger('kernel_connected.Kernel', {kernel: this});
406 this.events.trigger('kernel_connected.Kernel', {kernel: this});
407 // get kernel info so we know what state the kernel is in
407 // get kernel info so we know what state the kernel is in
408 var that = this;
408 var that = this;
409 this.kernel_info(function (reply) {
409 this.kernel_info(function (reply) {
410 that.info_reply = reply.content;
410 that.info_reply = reply.content;
411 that.events.trigger('kernel_ready.Kernel', {kernel: that});
411 that.events.trigger('kernel_ready.Kernel', {kernel: that});
412 });
412 });
413 };
413 };
414
414
415 Kernel.prototype._kernel_dead = function () {
415 Kernel.prototype._kernel_dead = function () {
416 /**
416 /**
417 * Perform necessary tasks after the kernel has died. This closing
417 * Perform necessary tasks after the kernel has died. This closing
418 * communication channels to the kernel if they are still somehow
418 * communication channels to the kernel if they are still somehow
419 * open.
419 * open.
420 *
420 *
421 * @function _kernel_dead
421 * @function _kernel_dead
422 */
422 */
423 this.stop_channels();
423 this.stop_channels();
424 };
424 };
425
425
426 Kernel.prototype.start_channels = function () {
426 Kernel.prototype.start_channels = function () {
427 /**
427 /**
428 * Start the websocket channels.
428 * Start the websocket channels.
429 * Will stop and restart them if they already exist.
429 * Will stop and restart them if they already exist.
430 *
430 *
431 * @function start_channels
431 * @function start_channels
432 */
432 */
433 var that = this;
433 var that = this;
434 this.stop_channels();
434 this.stop_channels();
435 var ws_host_url = this.ws_url + this.kernel_url;
435 var ws_host_url = this.ws_url + this.kernel_url;
436
436
437 console.log("Starting WebSockets:", ws_host_url);
437 console.log("Starting WebSockets:", ws_host_url);
438
438
439 this.ws = new this.WebSocket([
439 this.ws = new this.WebSocket([
440 that.ws_url,
440 that.ws_url,
441 utils.url_join_encode(that.kernel_url, 'channels'),
441 utils.url_join_encode(that.kernel_url, 'channels'),
442 "?session_id=" + that.session_id
442 "?session_id=" + that.session_id
443 ].join('')
443 ].join('')
444 );
444 );
445
445
446 var already_called_onclose = false; // only alert once
446 var already_called_onclose = false; // only alert once
447 var ws_closed_early = function(evt){
447 var ws_closed_early = function(evt){
448 if (already_called_onclose){
448 if (already_called_onclose){
449 return;
449 return;
450 }
450 }
451 already_called_onclose = true;
451 already_called_onclose = true;
452 if ( ! evt.wasClean ){
452 if ( ! evt.wasClean ){
453 // If the websocket was closed early, that could mean
453 // If the websocket was closed early, that could mean
454 // that the kernel is actually dead. Try getting
454 // that the kernel is actually dead. Try getting
455 // information about the kernel from the API call --
455 // information about the kernel from the API call --
456 // if that fails, then assume the kernel is dead,
456 // if that fails, then assume the kernel is dead,
457 // otherwise just follow the typical websocket closed
457 // otherwise just follow the typical websocket closed
458 // protocol.
458 // protocol.
459 that.get_info(function () {
459 that.get_info(function () {
460 that._ws_closed(ws_host_url, false);
460 that._ws_closed(ws_host_url, false);
461 }, function () {
461 }, function () {
462 that.events.trigger('kernel_dead.Kernel', {kernel: that});
462 that.events.trigger('kernel_dead.Kernel', {kernel: that});
463 that._kernel_dead();
463 that._kernel_dead();
464 });
464 });
465 }
465 }
466 };
466 };
467 var ws_closed_late = function(evt){
467 var ws_closed_late = function(evt){
468 if (already_called_onclose){
468 if (already_called_onclose){
469 return;
469 return;
470 }
470 }
471 already_called_onclose = true;
471 already_called_onclose = true;
472 if ( ! evt.wasClean ){
472 if ( ! evt.wasClean ){
473 that._ws_closed(ws_host_url, false);
473 that._ws_closed(ws_host_url, false);
474 }
474 }
475 };
475 };
476 var ws_error = function(evt){
476 var ws_error = function(evt){
477 if (already_called_onclose){
477 if (already_called_onclose){
478 return;
478 return;
479 }
479 }
480 already_called_onclose = true;
480 already_called_onclose = true;
481 that._ws_closed(ws_host_url, true);
481 that._ws_closed(ws_host_url, true);
482 };
482 };
483
483
484 this.ws.onopen = $.proxy(this._ws_opened, this);
484 this.ws.onopen = $.proxy(this._ws_opened, this);
485 this.ws.onclose = ws_closed_early;
485 this.ws.onclose = ws_closed_early;
486 this.ws.onerror = ws_error;
486 this.ws.onerror = ws_error;
487 // switch from early-close to late-close message after 1s
487 // switch from early-close to late-close message after 1s
488 setTimeout(function() {
488 setTimeout(function() {
489 if (that.ws !== null) {
489 if (that.ws !== null) {
490 that.ws.onclose = ws_closed_late;
490 that.ws.onclose = ws_closed_late;
491 }
491 }
492 }, 1000);
492 }, 1000);
493 this.ws.onmessage = $.proxy(this._handle_ws_message, this);
493 this.ws.onmessage = $.proxy(this._handle_ws_message, this);
494 };
494 };
495
495
496 Kernel.prototype._ws_opened = function (evt) {
496 Kernel.prototype._ws_opened = function (evt) {
497 /**
497 /**
498 * Handle a websocket entering the open state,
498 * Handle a websocket entering the open state,
499 * signaling that the kernel is connected when websocket is open.
499 * signaling that the kernel is connected when websocket is open.
500 *
500 *
501 * @function _ws_opened
501 * @function _ws_opened
502 */
502 */
503 if (this.is_connected()) {
503 if (this.is_connected()) {
504 // all events ready, trigger started event.
504 // all events ready, trigger started event.
505 this._kernel_connected();
505 this._kernel_connected();
506 }
506 }
507 };
507 };
508
508
509 Kernel.prototype._ws_closed = function(ws_url, error) {
509 Kernel.prototype._ws_closed = function(ws_url, error) {
510 /**
510 /**
511 * Handle a websocket entering the closed state. If the websocket
511 * Handle a websocket entering the closed state. If the websocket
512 * was not closed due to an error, try to reconnect to the kernel.
512 * was not closed due to an error, try to reconnect to the kernel.
513 *
513 *
514 * @function _ws_closed
514 * @function _ws_closed
515 * @param {string} ws_url - the websocket url
515 * @param {string} ws_url - the websocket url
516 * @param {bool} error - whether the connection was closed due to an error
516 * @param {bool} error - whether the connection was closed due to an error
517 */
517 */
518 this.stop_channels();
518 this.stop_channels();
519
519
520 this.events.trigger('kernel_disconnected.Kernel', {kernel: this});
520 this.events.trigger('kernel_disconnected.Kernel', {kernel: this});
521 if (error) {
521 if (error) {
522 console.log('WebSocket connection failed: ', ws_url);
522 console.log('WebSocket connection failed: ', ws_url);
523 this.events.trigger('kernel_connection_failed.Kernel', {kernel: this, ws_url: ws_url, attempt: this._reconnect_attempt});
523 this.events.trigger('kernel_connection_failed.Kernel', {kernel: this, ws_url: ws_url, attempt: this._reconnect_attempt});
524 }
524 }
525 this._schedule_reconnect();
525 this._schedule_reconnect();
526 };
526 };
527
527
528 Kernel.prototype._schedule_reconnect = function () {
528 Kernel.prototype._schedule_reconnect = function () {
529 /**
529 /**
530 * function to call when kernel connection is lost
530 * function to call when kernel connection is lost
531 * schedules reconnect, or fires 'connection_dead' if reconnect limit is hit
531 * schedules reconnect, or fires 'connection_dead' if reconnect limit is hit
532 */
532 */
533 if (this._reconnect_attempt < this.reconnect_limit) {
533 if (this._reconnect_attempt < this.reconnect_limit) {
534 var timeout = Math.pow(2, this._reconnect_attempt);
534 var timeout = Math.pow(2, this._reconnect_attempt);
535 console.log("Connection lost, reconnecting in " + timeout + " seconds.");
535 console.log("Connection lost, reconnecting in " + timeout + " seconds.");
536 setTimeout($.proxy(this.reconnect, this), 1e3 * timeout);
536 setTimeout($.proxy(this.reconnect, this), 1e3 * timeout);
537 } else {
537 } else {
538 this.events.trigger('kernel_connection_dead.Kernel', {
538 this.events.trigger('kernel_connection_dead.Kernel', {
539 kernel: this,
539 kernel: this,
540 reconnect_attempt: this._reconnect_attempt,
540 reconnect_attempt: this._reconnect_attempt,
541 });
541 });
542 console.log("Failed to reconnect, giving up.");
542 console.log("Failed to reconnect, giving up.");
543 }
543 }
544 };
544 };
545
545
546 Kernel.prototype.stop_channels = function () {
546 Kernel.prototype.stop_channels = function () {
547 /**
547 /**
548 * Close the websocket. After successful close, the value
548 * Close the websocket. After successful close, the value
549 * in `this.ws` will be null.
549 * in `this.ws` will be null.
550 *
550 *
551 * @function stop_channels
551 * @function stop_channels
552 */
552 */
553 var that = this;
553 var that = this;
554 var close = function () {
554 var close = function () {
555 if (that.ws && that.ws.readyState === WebSocket.CLOSED) {
555 if (that.ws && that.ws.readyState === WebSocket.CLOSED) {
556 that.ws = null;
556 that.ws = null;
557 }
557 }
558 };
558 };
559 if (this.ws !== null) {
559 if (this.ws !== null) {
560 if (this.ws.readyState === WebSocket.OPEN) {
560 if (this.ws.readyState === WebSocket.OPEN) {
561 this.ws.onclose = close;
561 this.ws.onclose = close;
562 this.ws.close();
562 this.ws.close();
563 } else {
563 } else {
564 close();
564 close();
565 }
565 }
566 }
566 }
567 };
567 };
568
568
569 Kernel.prototype.is_connected = function () {
569 Kernel.prototype.is_connected = function () {
570 /**
570 /**
571 * Check whether there is a connection to the kernel. This
571 * Check whether there is a connection to the kernel. This
572 * function only returns true if websocket has been
572 * function only returns true if websocket has been
573 * created and has a state of WebSocket.OPEN.
573 * created and has a state of WebSocket.OPEN.
574 *
574 *
575 * @function is_connected
575 * @function is_connected
576 * @returns {bool} - whether there is a connection
576 * @returns {bool} - whether there is a connection
577 */
577 */
578 // if any channel is not ready, then we're not connected
578 // if any channel is not ready, then we're not connected
579 if (this.ws === null) {
579 if (this.ws === null) {
580 return false;
580 return false;
581 }
581 }
582 if (this.ws.readyState !== WebSocket.OPEN) {
582 if (this.ws.readyState !== WebSocket.OPEN) {
583 return false;
583 return false;
584 }
584 }
585 return true;
585 return true;
586 };
586 };
587
587
588 Kernel.prototype.is_fully_disconnected = function () {
588 Kernel.prototype.is_fully_disconnected = function () {
589 /**
589 /**
590 * Check whether the connection to the kernel has been completely
590 * Check whether the connection to the kernel has been completely
591 * severed. This function only returns true if all channel objects
591 * severed. This function only returns true if all channel objects
592 * are null.
592 * are null.
593 *
593 *
594 * @function is_fully_disconnected
594 * @function is_fully_disconnected
595 * @returns {bool} - whether the kernel is fully disconnected
595 * @returns {bool} - whether the kernel is fully disconnected
596 */
596 */
597 return (this.ws === null);
597 return (this.ws === null);
598 };
598 };
599
599
600 Kernel.prototype.send_shell_message = function (msg_type, content, callbacks, metadata, buffers) {
600 Kernel.prototype.send_shell_message = function (msg_type, content, callbacks, metadata, buffers) {
601 /**
601 /**
602 * Send a message on the Kernel's shell channel
602 * Send a message on the Kernel's shell channel
603 *
603 *
604 * @function send_shell_message
604 * @function send_shell_message
605 */
605 */
606 if (!this.is_connected()) {
606 if (!this.is_connected()) {
607 throw new Error("kernel is not connected");
607 throw new Error("kernel is not connected");
608 }
608 }
609 var msg = this._get_msg(msg_type, content, metadata, buffers);
609 var msg = this._get_msg(msg_type, content, metadata, buffers);
610 msg.channel = 'shell';
610 msg.channel = 'shell';
611 this.ws.send(serialize.serialize(msg));
611 this.ws.send(serialize.serialize(msg));
612 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
612 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
613 return msg.header.msg_id;
613 return msg.header.msg_id;
614 };
614 };
615
615
616 Kernel.prototype.kernel_info = function (callback) {
616 Kernel.prototype.kernel_info = function (callback) {
617 /**
617 /**
618 * Get kernel info
618 * Get kernel info
619 *
619 *
620 * @function kernel_info
620 * @function kernel_info
621 * @param callback {function}
621 * @param callback {function}
622 *
622 *
623 * When calling this method, pass a callback function that expects one argument.
623 * When calling this method, pass a callback function that expects one argument.
624 * The callback will be passed the complete `kernel_info_reply` message documented
624 * The callback will be passed the complete `kernel_info_reply` message documented
625 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#kernel-info)
625 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#kernel-info)
626 */
626 */
627 var callbacks;
627 var callbacks;
628 if (callback) {
628 if (callback) {
629 callbacks = { shell : { reply : callback } };
629 callbacks = { shell : { reply : callback } };
630 }
630 }
631 return this.send_shell_message("kernel_info_request", {}, callbacks);
631 return this.send_shell_message("kernel_info_request", {}, callbacks);
632 };
632 };
633
633
634 Kernel.prototype.inspect = function (code, cursor_pos, callback) {
634 Kernel.prototype.inspect = function (code, cursor_pos, callback) {
635 /**
635 /**
636 * Get info on an object
636 * Get info on an object
637 *
637 *
638 * When calling this method, pass a callback function that expects one argument.
638 * When calling this method, pass a callback function that expects one argument.
639 * The callback will be passed the complete `inspect_reply` message documented
639 * The callback will be passed the complete `inspect_reply` message documented
640 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#object-information)
640 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#object-information)
641 *
641 *
642 * @function inspect
642 * @function inspect
643 * @param code {string}
643 * @param code {string}
644 * @param cursor_pos {integer}
644 * @param cursor_pos {integer}
645 * @param callback {function}
645 * @param callback {function}
646 */
646 */
647 var callbacks;
647 var callbacks;
648 if (callback) {
648 if (callback) {
649 callbacks = { shell : { reply : callback } };
649 callbacks = { shell : { reply : callback } };
650 }
650 }
651
651
652 var content = {
652 var content = {
653 code : code,
653 code : code,
654 cursor_pos : cursor_pos,
654 cursor_pos : cursor_pos,
655 detail_level : 0
655 detail_level : 0
656 };
656 };
657 return this.send_shell_message("inspect_request", content, callbacks);
657 return this.send_shell_message("inspect_request", content, callbacks);
658 };
658 };
659
659
660 Kernel.prototype.execute = function (code, callbacks, options) {
660 Kernel.prototype.execute = function (code, callbacks, options) {
661 /**
661 /**
662 * Execute given code into kernel, and pass result to callback.
662 * Execute given code into kernel, and pass result to callback.
663 *
663 *
664 * @async
664 * @async
665 * @function execute
665 * @function execute
666 * @param {string} code
666 * @param {string} code
667 * @param [callbacks] {Object} With the following keys (all optional)
667 * @param [callbacks] {Object} With the following keys (all optional)
668 * @param callbacks.shell.reply {function}
668 * @param callbacks.shell.reply {function}
669 * @param callbacks.shell.payload.[payload_name] {function}
669 * @param callbacks.shell.payload.[payload_name] {function}
670 * @param callbacks.iopub.output {function}
670 * @param callbacks.iopub.output {function}
671 * @param callbacks.iopub.clear_output {function}
671 * @param callbacks.iopub.clear_output {function}
672 * @param callbacks.input {function}
672 * @param callbacks.input {function}
673 * @param {object} [options]
673 * @param {object} [options]
674 * @param [options.silent=false] {Boolean}
674 * @param [options.silent=false] {Boolean}
675 * @param [options.user_expressions=empty_dict] {Dict}
675 * @param [options.user_expressions=empty_dict] {Dict}
676 * @param [options.allow_stdin=false] {Boolean} true|false
676 * @param [options.allow_stdin=false] {Boolean} true|false
677 *
677 *
678 * @example
678 * @example
679 *
679 *
680 * The options object should contain the options for the execute
680 * The options object should contain the options for the execute
681 * call. Its default values are:
681 * call. Its default values are:
682 *
682 *
683 * options = {
683 * options = {
684 * silent : true,
684 * silent : true,
685 * user_expressions : {},
685 * user_expressions : {},
686 * allow_stdin : false
686 * allow_stdin : false
687 * }
687 * }
688 *
688 *
689 * When calling this method pass a callbacks structure of the
689 * When calling this method pass a callbacks structure of the
690 * form:
690 * form:
691 *
691 *
692 * callbacks = {
692 * callbacks = {
693 * shell : {
693 * shell : {
694 * reply : execute_reply_callback,
694 * reply : execute_reply_callback,
695 * payload : {
695 * payload : {
696 * set_next_input : set_next_input_callback,
696 * set_next_input : set_next_input_callback,
697 * }
697 * }
698 * },
698 * },
699 * iopub : {
699 * iopub : {
700 * output : output_callback,
700 * output : output_callback,
701 * clear_output : clear_output_callback,
701 * clear_output : clear_output_callback,
702 * },
702 * },
703 * input : raw_input_callback
703 * input : raw_input_callback
704 * }
704 * }
705 *
705 *
706 * Each callback will be passed the entire message as a single
706 * Each callback will be passed the entire message as a single
707 * arugment. Payload handlers will be passed the corresponding
707 * arugment. Payload handlers will be passed the corresponding
708 * payload and the execute_reply message.
708 * payload and the execute_reply message.
709 */
709 */
710 var content = {
710 var content = {
711 code : code,
711 code : code,
712 silent : true,
712 silent : true,
713 store_history : false,
713 store_history : false,
714 user_expressions : {},
714 user_expressions : {},
715 allow_stdin : false
715 allow_stdin : false
716 };
716 };
717 callbacks = callbacks || {};
717 callbacks = callbacks || {};
718 if (callbacks.input !== undefined) {
718 if (callbacks.input !== undefined) {
719 content.allow_stdin = true;
719 content.allow_stdin = true;
720 }
720 }
721 $.extend(true, content, options);
721 $.extend(true, content, options);
722 this.events.trigger('execution_request.Kernel', {kernel: this, content: content});
722 this.events.trigger('execution_request.Kernel', {kernel: this, content: content});
723 return this.send_shell_message("execute_request", content, callbacks);
723 return this.send_shell_message("execute_request", content, callbacks);
724 };
724 };
725
725
726 /**
726 /**
727 * When calling this method, pass a function to be called with the
727 * When calling this method, pass a function to be called with the
728 * `complete_reply` message as its only argument when it arrives.
728 * `complete_reply` message as its only argument when it arrives.
729 *
729 *
730 * `complete_reply` is documented
730 * `complete_reply` is documented
731 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#complete)
731 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#complete)
732 *
732 *
733 * @function complete
733 * @function complete
734 * @param code {string}
734 * @param code {string}
735 * @param cursor_pos {integer}
735 * @param cursor_pos {integer}
736 * @param callback {function}
736 * @param callback {function}
737 */
737 */
738 Kernel.prototype.complete = function (code, cursor_pos, callback) {
738 Kernel.prototype.complete = function (code, cursor_pos, callback) {
739 var callbacks;
739 var callbacks;
740 if (callback) {
740 if (callback) {
741 callbacks = { shell : { reply : callback } };
741 callbacks = { shell : { reply : callback } };
742 }
742 }
743 var content = {
743 var content = {
744 code : code,
744 code : code,
745 cursor_pos : cursor_pos
745 cursor_pos : cursor_pos
746 };
746 };
747 return this.send_shell_message("complete_request", content, callbacks);
747 return this.send_shell_message("complete_request", content, callbacks);
748 };
748 };
749
749
750 /**
750 /**
751 * @function send_input_reply
751 * @function send_input_reply
752 */
752 */
753 Kernel.prototype.send_input_reply = function (input) {
753 Kernel.prototype.send_input_reply = function (input) {
754 if (!this.is_connected()) {
754 if (!this.is_connected()) {
755 throw new Error("kernel is not connected");
755 throw new Error("kernel is not connected");
756 }
756 }
757 var content = {
757 var content = {
758 value : input
758 value : input
759 };
759 };
760 this.events.trigger('input_reply.Kernel', {kernel: this, content: content});
760 this.events.trigger('input_reply.Kernel', {kernel: this, content: content});
761 var msg = this._get_msg("input_reply", content);
761 var msg = this._get_msg("input_reply", content);
762 msg.channel = 'stdin';
762 msg.channel = 'stdin';
763 this.ws.send(serialize.serialize(msg));
763 this.ws.send(serialize.serialize(msg));
764 return msg.header.msg_id;
764 return msg.header.msg_id;
765 };
765 };
766
766
767 /**
767 /**
768 * @function register_iopub_handler
768 * @function register_iopub_handler
769 */
769 */
770 Kernel.prototype.register_iopub_handler = function (msg_type, callback) {
770 Kernel.prototype.register_iopub_handler = function (msg_type, callback) {
771 this._iopub_handlers[msg_type] = callback;
771 this._iopub_handlers[msg_type] = callback;
772 };
772 };
773
773
774 /**
774 /**
775 * Get the iopub handler for a specific message type.
775 * Get the iopub handler for a specific message type.
776 *
776 *
777 * @function get_iopub_handler
777 * @function get_iopub_handler
778 */
778 */
779 Kernel.prototype.get_iopub_handler = function (msg_type) {
779 Kernel.prototype.get_iopub_handler = function (msg_type) {
780 return this._iopub_handlers[msg_type];
780 return this._iopub_handlers[msg_type];
781 };
781 };
782
782
783 /**
783 /**
784 * Get callbacks for a specific message.
784 * Get callbacks for a specific message.
785 *
785 *
786 * @function get_callbacks_for_msg
786 * @function get_callbacks_for_msg
787 */
787 */
788 Kernel.prototype.get_callbacks_for_msg = function (msg_id) {
788 Kernel.prototype.get_callbacks_for_msg = function (msg_id) {
789 if (msg_id == this.last_msg_id) {
789 if (msg_id == this.last_msg_id) {
790 return this.last_msg_callbacks;
790 return this.last_msg_callbacks;
791 } else {
791 } else {
792 return this._msg_callbacks[msg_id];
792 return this._msg_callbacks[msg_id];
793 }
793 }
794 };
794 };
795
795
796 /**
796 /**
797 * Clear callbacks for a specific message.
797 * Clear callbacks for a specific message.
798 *
798 *
799 * @function clear_callbacks_for_msg
799 * @function clear_callbacks_for_msg
800 */
800 */
801 Kernel.prototype.clear_callbacks_for_msg = function (msg_id) {
801 Kernel.prototype.clear_callbacks_for_msg = function (msg_id) {
802 if (this._msg_callbacks[msg_id] !== undefined ) {
802 if (this._msg_callbacks[msg_id] !== undefined ) {
803 delete this._msg_callbacks[msg_id];
803 delete this._msg_callbacks[msg_id];
804 }
804 }
805 };
805 };
806
806
807 /**
807 /**
808 * @function _finish_shell
808 * @function _finish_shell
809 */
809 */
810 Kernel.prototype._finish_shell = function (msg_id) {
810 Kernel.prototype._finish_shell = function (msg_id) {
811 var callbacks = this._msg_callbacks[msg_id];
811 var callbacks = this._msg_callbacks[msg_id];
812 if (callbacks !== undefined) {
812 if (callbacks !== undefined) {
813 callbacks.shell_done = true;
813 callbacks.shell_done = true;
814 if (callbacks.iopub_done) {
814 if (callbacks.iopub_done) {
815 this.clear_callbacks_for_msg(msg_id);
815 this.clear_callbacks_for_msg(msg_id);
816 }
816 }
817 }
817 }
818 };
818 };
819
819
820 /**
820 /**
821 * @function _finish_iopub
821 * @function _finish_iopub
822 */
822 */
823 Kernel.prototype._finish_iopub = function (msg_id) {
823 Kernel.prototype._finish_iopub = function (msg_id) {
824 var callbacks = this._msg_callbacks[msg_id];
824 var callbacks = this._msg_callbacks[msg_id];
825 if (callbacks !== undefined) {
825 if (callbacks !== undefined) {
826 callbacks.iopub_done = true;
826 callbacks.iopub_done = true;
827 if (callbacks.shell_done) {
827 if (callbacks.shell_done) {
828 this.clear_callbacks_for_msg(msg_id);
828 this.clear_callbacks_for_msg(msg_id);
829 }
829 }
830 }
830 }
831 };
831 };
832
832
833 /**
833 /**
834 * Set callbacks for a particular message.
834 * Set callbacks for a particular message.
835 * Callbacks should be a struct of the following form:
835 * Callbacks should be a struct of the following form:
836 * shell : {
836 * shell : {
837 *
837 *
838 * }
838 * }
839 *
839 *
840 * @function set_callbacks_for_msg
840 * @function set_callbacks_for_msg
841 */
841 */
842 Kernel.prototype.set_callbacks_for_msg = function (msg_id, callbacks) {
842 Kernel.prototype.set_callbacks_for_msg = function (msg_id, callbacks) {
843 this.last_msg_id = msg_id;
843 this.last_msg_id = msg_id;
844 if (callbacks) {
844 if (callbacks) {
845 // shallow-copy mapping, because we will modify it at the top level
845 // shallow-copy mapping, because we will modify it at the top level
846 var cbcopy = this._msg_callbacks[msg_id] = this.last_msg_callbacks = {};
846 var cbcopy = this._msg_callbacks[msg_id] = this.last_msg_callbacks = {};
847 cbcopy.shell = callbacks.shell;
847 cbcopy.shell = callbacks.shell;
848 cbcopy.iopub = callbacks.iopub;
848 cbcopy.iopub = callbacks.iopub;
849 cbcopy.input = callbacks.input;
849 cbcopy.input = callbacks.input;
850 cbcopy.shell_done = (!callbacks.shell);
850 cbcopy.shell_done = (!callbacks.shell);
851 cbcopy.iopub_done = (!callbacks.iopub);
851 cbcopy.iopub_done = (!callbacks.iopub);
852 } else {
852 } else {
853 this.last_msg_callbacks = {};
853 this.last_msg_callbacks = {};
854 }
854 }
855 };
855 };
856
856
857 Kernel.prototype._handle_ws_message = function (e) {
857 Kernel.prototype._handle_ws_message = function (e) {
858 var that = this;
858 this._msg_queue = this._msg_queue.then(function() {
859 this._msg_queue = this._msg_queue.then(function() {
859 return serialize.deserialize(e.data);
860 return serialize.deserialize(e.data);
860 }).then($.proxy(this._finish_ws_message, this))
861 }).then(function(msg) {return that._finish_ws_message(msg);})
861 .catch(utils.reject("Couldn't process kernel message", true));
862 .catch(utils.reject("Couldn't process kernel message", true));
862 };
863 };
863
864
864 Kernel.prototype._finish_ws_message = function (msg) {
865 Kernel.prototype._finish_ws_message = function (msg) {
865 switch (msg.channel) {
866 switch (msg.channel) {
866 case 'shell':
867 case 'shell':
867 this._handle_shell_reply(msg);
868 return this._handle_shell_reply(msg);
868 break;
869 break;
869 case 'iopub':
870 case 'iopub':
870 this._handle_iopub_message(msg);
871 return this._handle_iopub_message(msg);
871 break;
872 break;
872 case 'stdin':
873 case 'stdin':
873 this._handle_input_request(msg);
874 return this._handle_input_request(msg);
874 break;
875 break;
875 default:
876 default:
876 console.error("unrecognized message channel", msg.channel, msg);
877 console.error("unrecognized message channel", msg.channel, msg);
877 }
878 }
878 };
879 };
879
880
880 Kernel.prototype._handle_shell_reply = function (reply) {
881 Kernel.prototype._handle_shell_reply = function (reply) {
881 this.events.trigger('shell_reply.Kernel', {kernel: this, reply:reply});
882 this.events.trigger('shell_reply.Kernel', {kernel: this, reply:reply});
883 var that = this;
882 var content = reply.content;
884 var content = reply.content;
883 var metadata = reply.metadata;
885 var metadata = reply.metadata;
884 var parent_id = reply.parent_header.msg_id;
886 var parent_id = reply.parent_header.msg_id;
885 var callbacks = this.get_callbacks_for_msg(parent_id);
887 var callbacks = this.get_callbacks_for_msg(parent_id);
888 var promise = Promise.resolve();
886 if (!callbacks || !callbacks.shell) {
889 if (!callbacks || !callbacks.shell) {
887 return;
890 return;
888 }
891 }
889 var shell_callbacks = callbacks.shell;
892 var shell_callbacks = callbacks.shell;
890
893
891 // signal that shell callbacks are done
894 // signal that shell callbacks are done
892 this._finish_shell(parent_id);
895 this._finish_shell(parent_id);
893
896
894 if (shell_callbacks.reply !== undefined) {
897 if (shell_callbacks.reply !== undefined) {
895 shell_callbacks.reply(reply);
898 promise = promise.then(function() {return shell_callbacks.reply(reply)});
896 }
899 }
897 if (content.payload && shell_callbacks.payload) {
900 if (content.payload && shell_callbacks.payload) {
898 this._handle_payloads(content.payload, shell_callbacks.payload, reply);
901 promise = promise.then(function() {
902 return that._handle_payloads(content.payload, shell_callbacks.payload, reply);
903 });
899 }
904 }
905 return promise;
900 };
906 };
901
907
902 /**
908 /**
903 * @function _handle_payloads
909 * @function _handle_payloads
904 */
910 */
905 Kernel.prototype._handle_payloads = function (payloads, payload_callbacks, msg) {
911 Kernel.prototype._handle_payloads = function (payloads, payload_callbacks, msg) {
912 var promise = [];
906 var l = payloads.length;
913 var l = payloads.length;
907 // Payloads are handled by triggering events because we don't want the Kernel
914 // Payloads are handled by triggering events because we don't want the Kernel
908 // to depend on the Notebook or Pager classes.
915 // to depend on the Notebook or Pager classes.
909 for (var i=0; i<l; i++) {
916 for (var i=0; i<l; i++) {
910 var payload = payloads[i];
917 var payload = payloads[i];
911 var callback = payload_callbacks[payload.source];
918 var callback = payload_callbacks[payload.source];
912 if (callback) {
919 if (callback) {
913 callback(payload, msg);
920 promise.push(callback(payload, msg));
914 }
921 }
915 }
922 }
923 return Promise.all(promise);
916 };
924 };
917
925
918 /**
926 /**
919 * @function _handle_status_message
927 * @function _handle_status_message
920 */
928 */
921 Kernel.prototype._handle_status_message = function (msg) {
929 Kernel.prototype._handle_status_message = function (msg) {
922 var execution_state = msg.content.execution_state;
930 var execution_state = msg.content.execution_state;
923 var parent_id = msg.parent_header.msg_id;
931 var parent_id = msg.parent_header.msg_id;
924
932
925 // dispatch status msg callbacks, if any
933 // dispatch status msg callbacks, if any
926 var callbacks = this.get_callbacks_for_msg(parent_id);
934 var callbacks = this.get_callbacks_for_msg(parent_id);
927 if (callbacks && callbacks.iopub && callbacks.iopub.status) {
935 if (callbacks && callbacks.iopub && callbacks.iopub.status) {
928 try {
936 try {
929 callbacks.iopub.status(msg);
937 callbacks.iopub.status(msg);
930 } catch (e) {
938 } catch (e) {
931 console.log("Exception in status msg handler", e, e.stack);
939 console.log("Exception in status msg handler", e, e.stack);
932 }
940 }
933 }
941 }
934
942
935 if (execution_state === 'busy') {
943 if (execution_state === 'busy') {
936 this.events.trigger('kernel_busy.Kernel', {kernel: this});
944 this.events.trigger('kernel_busy.Kernel', {kernel: this});
937
945
938 } else if (execution_state === 'idle') {
946 } else if (execution_state === 'idle') {
939 // signal that iopub callbacks are (probably) done
947 // signal that iopub callbacks are (probably) done
940 // async output may still arrive,
948 // async output may still arrive,
941 // but only for the most recent request
949 // but only for the most recent request
942 this._finish_iopub(parent_id);
950 this._finish_iopub(parent_id);
943
951
944 // trigger status_idle event
952 // trigger status_idle event
945 this.events.trigger('kernel_idle.Kernel', {kernel: this});
953 this.events.trigger('kernel_idle.Kernel', {kernel: this});
946
954
947 } else if (execution_state === 'starting') {
955 } else if (execution_state === 'starting') {
948 this.events.trigger('kernel_starting.Kernel', {kernel: this});
956 this.events.trigger('kernel_starting.Kernel', {kernel: this});
949 var that = this;
957 var that = this;
950 this.kernel_info(function (reply) {
958 this.kernel_info(function (reply) {
951 that.info_reply = reply.content;
959 that.info_reply = reply.content;
952 that.events.trigger('kernel_ready.Kernel', {kernel: that});
960 that.events.trigger('kernel_ready.Kernel', {kernel: that});
953 });
961 });
954
962
955 } else if (execution_state === 'restarting') {
963 } else if (execution_state === 'restarting') {
956 // autorestarting is distinct from restarting,
964 // autorestarting is distinct from restarting,
957 // in that it means the kernel died and the server is restarting it.
965 // in that it means the kernel died and the server is restarting it.
958 // kernel_restarting sets the notification widget,
966 // kernel_restarting sets the notification widget,
959 // autorestart shows the more prominent dialog.
967 // autorestart shows the more prominent dialog.
960 this._autorestart_attempt = this._autorestart_attempt + 1;
968 this._autorestart_attempt = this._autorestart_attempt + 1;
961 this.events.trigger('kernel_restarting.Kernel', {kernel: this});
969 this.events.trigger('kernel_restarting.Kernel', {kernel: this});
962 this.events.trigger('kernel_autorestarting.Kernel', {kernel: this, attempt: this._autorestart_attempt});
970 this.events.trigger('kernel_autorestarting.Kernel', {kernel: this, attempt: this._autorestart_attempt});
963
971
964 } else if (execution_state === 'dead') {
972 } else if (execution_state === 'dead') {
965 this.events.trigger('kernel_dead.Kernel', {kernel: this});
973 this.events.trigger('kernel_dead.Kernel', {kernel: this});
966 this._kernel_dead();
974 this._kernel_dead();
967 }
975 }
968 };
976 };
969
977
970 /**
978 /**
971 * Handle clear_output message
979 * Handle clear_output message
972 *
980 *
973 * @function _handle_clear_output
981 * @function _handle_clear_output
974 */
982 */
975 Kernel.prototype._handle_clear_output = function (msg) {
983 Kernel.prototype._handle_clear_output = function (msg) {
976 var callbacks = this.get_callbacks_for_msg(msg.parent_header.msg_id);
984 var callbacks = this.get_callbacks_for_msg(msg.parent_header.msg_id);
977 if (!callbacks || !callbacks.iopub) {
985 if (!callbacks || !callbacks.iopub) {
978 return;
986 return;
979 }
987 }
980 var callback = callbacks.iopub.clear_output;
988 var callback = callbacks.iopub.clear_output;
981 if (callback) {
989 if (callback) {
982 callback(msg);
990 callback(msg);
983 }
991 }
984 };
992 };
985
993
986 /**
994 /**
987 * handle an output message (execute_result, display_data, etc.)
995 * handle an output message (execute_result, display_data, etc.)
988 *
996 *
989 * @function _handle_output_message
997 * @function _handle_output_message
990 */
998 */
991 Kernel.prototype._handle_output_message = function (msg) {
999 Kernel.prototype._handle_output_message = function (msg) {
992 var callbacks = this.get_callbacks_for_msg(msg.parent_header.msg_id);
1000 var callbacks = this.get_callbacks_for_msg(msg.parent_header.msg_id);
993 if (!callbacks || !callbacks.iopub) {
1001 if (!callbacks || !callbacks.iopub) {
994 // The message came from another client. Let the UI decide what to
1002 // The message came from another client. Let the UI decide what to
995 // do with it.
1003 // do with it.
996 this.events.trigger('received_unsolicited_message.Kernel', msg);
1004 this.events.trigger('received_unsolicited_message.Kernel', msg);
997 return;
1005 return;
998 }
1006 }
999 var callback = callbacks.iopub.output;
1007 var callback = callbacks.iopub.output;
1000 if (callback) {
1008 if (callback) {
1001 callback(msg);
1009 callback(msg);
1002 }
1010 }
1003 };
1011 };
1004
1012
1005 /**
1013 /**
1006 * Handle an input message (execute_input).
1014 * Handle an input message (execute_input).
1007 *
1015 *
1008 * @function _handle_input message
1016 * @function _handle_input message
1009 */
1017 */
1010 Kernel.prototype._handle_input_message = function (msg) {
1018 Kernel.prototype._handle_input_message = function (msg) {
1011 var callbacks = this.get_callbacks_for_msg(msg.parent_header.msg_id);
1019 var callbacks = this.get_callbacks_for_msg(msg.parent_header.msg_id);
1012 if (!callbacks) {
1020 if (!callbacks) {
1013 // The message came from another client. Let the UI decide what to
1021 // The message came from another client. Let the UI decide what to
1014 // do with it.
1022 // do with it.
1015 this.events.trigger('received_unsolicited_message.Kernel', msg);
1023 this.events.trigger('received_unsolicited_message.Kernel', msg);
1016 }
1024 }
1017 };
1025 };
1018
1026
1019 /**
1027 /**
1020 * Dispatch IOPub messages to respective handlers. Each message
1028 * Dispatch IOPub messages to respective handlers. Each message
1021 * type should have a handler.
1029 * type should have a handler.
1022 *
1030 *
1023 * @function _handle_iopub_message
1031 * @function _handle_iopub_message
1024 */
1032 */
1025 Kernel.prototype._handle_iopub_message = function (msg) {
1033 Kernel.prototype._handle_iopub_message = function (msg) {
1026 var handler = this.get_iopub_handler(msg.header.msg_type);
1034 var handler = this.get_iopub_handler(msg.header.msg_type);
1027 if (handler !== undefined) {
1035 if (handler !== undefined) {
1028 handler(msg);
1036 return handler(msg);
1029 }
1037 }
1030 };
1038 };
1031
1039
1032 /**
1040 /**
1033 * @function _handle_input_request
1041 * @function _handle_input_request
1034 */
1042 */
1035 Kernel.prototype._handle_input_request = function (request) {
1043 Kernel.prototype._handle_input_request = function (request) {
1036 var header = request.header;
1044 var header = request.header;
1037 var content = request.content;
1045 var content = request.content;
1038 var metadata = request.metadata;
1046 var metadata = request.metadata;
1039 var msg_type = header.msg_type;
1047 var msg_type = header.msg_type;
1040 if (msg_type !== 'input_request') {
1048 if (msg_type !== 'input_request') {
1041 console.log("Invalid input request!", request);
1049 console.log("Invalid input request!", request);
1042 return;
1050 return;
1043 }
1051 }
1044 var callbacks = this.get_callbacks_for_msg(request.parent_header.msg_id);
1052 var callbacks = this.get_callbacks_for_msg(request.parent_header.msg_id);
1045 if (callbacks) {
1053 if (callbacks) {
1046 if (callbacks.input) {
1054 if (callbacks.input) {
1047 callbacks.input(request);
1055 callbacks.input(request);
1048 }
1056 }
1049 }
1057 }
1050 };
1058 };
1051
1059
1052 // Backwards compatability.
1060 // Backwards compatability.
1053 IPython.Kernel = Kernel;
1061 IPython.Kernel = Kernel;
1054
1062
1055 return {'Kernel': Kernel};
1063 return {'Kernel': Kernel};
1056 });
1064 });
General Comments 0
You need to be logged in to leave comments. Login now