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