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