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