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