##// END OF EJS Templates
Merge pull request #6497 from SylvainCorlay/_ws_opened...
Jonathan Frederic -
r17922:53c63dc6 merge
parent child Browse files
Show More
@@ -1,626 +1,626 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 // Initialization and connection.
13 // Initialization and connection.
14 /**
14 /**
15 * A Kernel Class to communicate with the Python kernel
15 * A Kernel Class to communicate with the Python kernel
16 * @Class Kernel
16 * @Class Kernel
17 */
17 */
18 var Kernel = function (kernel_service_url, ws_url, notebook, name) {
18 var Kernel = function (kernel_service_url, ws_url, notebook, name) {
19 this.events = notebook.events;
19 this.events = notebook.events;
20 this.kernel_id = null;
20 this.kernel_id = null;
21 this.shell_channel = null;
21 this.shell_channel = null;
22 this.iopub_channel = null;
22 this.iopub_channel = null;
23 this.stdin_channel = null;
23 this.stdin_channel = null;
24 this.kernel_service_url = kernel_service_url;
24 this.kernel_service_url = kernel_service_url;
25 this.name = name;
25 this.name = name;
26 this.ws_url = ws_url || IPython.utils.get_body_data("wsUrl");
26 this.ws_url = ws_url || IPython.utils.get_body_data("wsUrl");
27 if (!this.ws_url) {
27 if (!this.ws_url) {
28 // trailing 's' in https will become wss for secure web sockets
28 // trailing 's' in https will become wss for secure web sockets
29 this.ws_url = location.protocol.replace('http', 'ws') + "//" + location.host;
29 this.ws_url = location.protocol.replace('http', 'ws') + "//" + location.host;
30 }
30 }
31 this.running = false;
31 this.running = false;
32 this.username = "username";
32 this.username = "username";
33 this.session_id = utils.uuid();
33 this.session_id = utils.uuid();
34 this._msg_callbacks = {};
34 this._msg_callbacks = {};
35 this.post = $.post;
35 this.post = $.post;
36
36
37 if (typeof(WebSocket) !== 'undefined') {
37 if (typeof(WebSocket) !== 'undefined') {
38 this.WebSocket = WebSocket;
38 this.WebSocket = WebSocket;
39 } else if (typeof(MozWebSocket) !== 'undefined') {
39 } else if (typeof(MozWebSocket) !== 'undefined') {
40 this.WebSocket = MozWebSocket;
40 this.WebSocket = MozWebSocket;
41 } else {
41 } else {
42 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.');
42 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.');
43 }
43 }
44
44
45 this.bind_events();
45 this.bind_events();
46 this.init_iopub_handlers();
46 this.init_iopub_handlers();
47 this.comm_manager = new comm.CommManager(this);
47 this.comm_manager = new comm.CommManager(this);
48 this.widget_manager = new widgetmanager.WidgetManager(this.comm_manager, notebook);
48 this.widget_manager = new widgetmanager.WidgetManager(this.comm_manager, notebook);
49
49
50 this.last_msg_id = null;
50 this.last_msg_id = null;
51 this.last_msg_callbacks = {};
51 this.last_msg_callbacks = {};
52 };
52 };
53
53
54
54
55 Kernel.prototype._get_msg = function (msg_type, content, metadata) {
55 Kernel.prototype._get_msg = function (msg_type, content, metadata) {
56 var msg = {
56 var msg = {
57 header : {
57 header : {
58 msg_id : utils.uuid(),
58 msg_id : utils.uuid(),
59 username : this.username,
59 username : this.username,
60 session : this.session_id,
60 session : this.session_id,
61 msg_type : msg_type,
61 msg_type : msg_type,
62 version : "5.0"
62 version : "5.0"
63 },
63 },
64 metadata : metadata || {},
64 metadata : metadata || {},
65 content : content,
65 content : content,
66 parent_header : {}
66 parent_header : {}
67 };
67 };
68 return msg;
68 return msg;
69 };
69 };
70
70
71 Kernel.prototype.bind_events = function () {
71 Kernel.prototype.bind_events = function () {
72 var that = this;
72 var that = this;
73 this.events.on('send_input_reply.Kernel', function(evt, data) {
73 this.events.on('send_input_reply.Kernel', function(evt, data) {
74 that.send_input_reply(data);
74 that.send_input_reply(data);
75 });
75 });
76 };
76 };
77
77
78 // Initialize the iopub handlers
78 // Initialize the iopub handlers
79
79
80 Kernel.prototype.init_iopub_handlers = function () {
80 Kernel.prototype.init_iopub_handlers = function () {
81 var output_msg_types = ['stream', 'display_data', 'execute_result', 'error'];
81 var output_msg_types = ['stream', 'display_data', 'execute_result', 'error'];
82 this._iopub_handlers = {};
82 this._iopub_handlers = {};
83 this.register_iopub_handler('status', $.proxy(this._handle_status_message, this));
83 this.register_iopub_handler('status', $.proxy(this._handle_status_message, this));
84 this.register_iopub_handler('clear_output', $.proxy(this._handle_clear_output, this));
84 this.register_iopub_handler('clear_output', $.proxy(this._handle_clear_output, this));
85
85
86 for (var i=0; i < output_msg_types.length; i++) {
86 for (var i=0; i < output_msg_types.length; i++) {
87 this.register_iopub_handler(output_msg_types[i], $.proxy(this._handle_output_message, this));
87 this.register_iopub_handler(output_msg_types[i], $.proxy(this._handle_output_message, this));
88 }
88 }
89 };
89 };
90
90
91 /**
91 /**
92 * Start the Python kernel
92 * Start the Python kernel
93 * @method start
93 * @method start
94 */
94 */
95 Kernel.prototype.start = function (params) {
95 Kernel.prototype.start = function (params) {
96 params = params || {};
96 params = params || {};
97 if (!this.running) {
97 if (!this.running) {
98 var qs = $.param(params);
98 var qs = $.param(params);
99 this.post(utils.url_join_encode(this.kernel_service_url) + '?' + qs,
99 this.post(utils.url_join_encode(this.kernel_service_url) + '?' + qs,
100 $.proxy(this._kernel_started, this),
100 $.proxy(this._kernel_started, this),
101 'json'
101 'json'
102 );
102 );
103 }
103 }
104 };
104 };
105
105
106 /**
106 /**
107 * Restart the python kernel.
107 * Restart the python kernel.
108 *
108 *
109 * Emit a 'status_restarting.Kernel' event with
109 * Emit a 'status_restarting.Kernel' event with
110 * the current object as parameter
110 * the current object as parameter
111 *
111 *
112 * @method restart
112 * @method restart
113 */
113 */
114 Kernel.prototype.restart = function () {
114 Kernel.prototype.restart = function () {
115 this.events.trigger('status_restarting.Kernel', {kernel: this});
115 this.events.trigger('status_restarting.Kernel', {kernel: this});
116 if (this.running) {
116 if (this.running) {
117 this.stop_channels();
117 this.stop_channels();
118 this.post(utils.url_join_encode(this.kernel_url, "restart"),
118 this.post(utils.url_join_encode(this.kernel_url, "restart"),
119 $.proxy(this._kernel_started, this),
119 $.proxy(this._kernel_started, this),
120 'json'
120 'json'
121 );
121 );
122 }
122 }
123 };
123 };
124
124
125
125
126 Kernel.prototype._kernel_started = function (json) {
126 Kernel.prototype._kernel_started = function (json) {
127 console.log("Kernel started: ", json.id);
127 console.log("Kernel started: ", json.id);
128 this.running = true;
128 this.running = true;
129 this.kernel_id = json.id;
129 this.kernel_id = json.id;
130 this.kernel_url = utils.url_path_join(this.kernel_service_url, this.kernel_id);
130 this.kernel_url = utils.url_path_join(this.kernel_service_url, this.kernel_id);
131 this.start_channels();
131 this.start_channels();
132 };
132 };
133
133
134
134
135 Kernel.prototype._websocket_closed = function(ws_url, early) {
135 Kernel.prototype._websocket_closed = function(ws_url, early) {
136 this.stop_channels();
136 this.stop_channels();
137 this.events.trigger('websocket_closed.Kernel',
137 this.events.trigger('websocket_closed.Kernel',
138 {ws_url: ws_url, kernel: this, early: early}
138 {ws_url: ws_url, kernel: this, early: early}
139 );
139 );
140 };
140 };
141
141
142 /**
142 /**
143 * Start the `shell`and `iopub` channels.
143 * Start the `shell`and `iopub` channels.
144 * Will stop and restart them if they already exist.
144 * Will stop and restart them if they already exist.
145 *
145 *
146 * @method start_channels
146 * @method start_channels
147 */
147 */
148 Kernel.prototype.start_channels = function () {
148 Kernel.prototype.start_channels = function () {
149 var that = this;
149 var that = this;
150 this.stop_channels();
150 this.stop_channels();
151 var ws_host_url = this.ws_url + this.kernel_url;
151 var ws_host_url = this.ws_url + this.kernel_url;
152 console.log("Starting WebSockets:", ws_host_url);
152 console.log("Starting WebSockets:", ws_host_url);
153 this.shell_channel = new this.WebSocket(
153 this.shell_channel = new this.WebSocket(
154 this.ws_url + utils.url_join_encode(this.kernel_url, "shell")
154 this.ws_url + utils.url_join_encode(this.kernel_url, "shell")
155 );
155 );
156 this.stdin_channel = new this.WebSocket(
156 this.stdin_channel = new this.WebSocket(
157 this.ws_url + utils.url_join_encode(this.kernel_url, "stdin")
157 this.ws_url + utils.url_join_encode(this.kernel_url, "stdin")
158 );
158 );
159 this.iopub_channel = new this.WebSocket(
159 this.iopub_channel = new this.WebSocket(
160 this.ws_url + utils.url_join_encode(this.kernel_url, "iopub")
160 this.ws_url + utils.url_join_encode(this.kernel_url, "iopub")
161 );
161 );
162
162
163 var already_called_onclose = false; // only alert once
163 var already_called_onclose = false; // only alert once
164 var ws_closed_early = function(evt){
164 var ws_closed_early = function(evt){
165 if (already_called_onclose){
165 if (already_called_onclose){
166 return;
166 return;
167 }
167 }
168 already_called_onclose = true;
168 already_called_onclose = true;
169 if ( ! evt.wasClean ){
169 if ( ! evt.wasClean ){
170 that._websocket_closed(ws_host_url, true);
170 that._websocket_closed(ws_host_url, true);
171 }
171 }
172 };
172 };
173 var ws_closed_late = function(evt){
173 var ws_closed_late = function(evt){
174 if (already_called_onclose){
174 if (already_called_onclose){
175 return;
175 return;
176 }
176 }
177 already_called_onclose = true;
177 already_called_onclose = true;
178 if ( ! evt.wasClean ){
178 if ( ! evt.wasClean ){
179 that._websocket_closed(ws_host_url, false);
179 that._websocket_closed(ws_host_url, false);
180 }
180 }
181 };
181 };
182 var ws_error = function(evt){
182 var ws_error = function(evt){
183 if (already_called_onclose){
183 if (already_called_onclose){
184 return;
184 return;
185 }
185 }
186 already_called_onclose = true;
186 already_called_onclose = true;
187 that._websocket_closed(ws_host_url, false);
187 that._websocket_closed(ws_host_url, false);
188 };
188 };
189 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
189 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
190 for (var i=0; i < channels.length; i++) {
190 for (var i=0; i < channels.length; i++) {
191 channels[i].onopen = $.proxy(this._ws_opened, this);
191 channels[i].onopen = $.proxy(this._ws_opened, this);
192 channels[i].onclose = ws_closed_early;
192 channels[i].onclose = ws_closed_early;
193 channels[i].onerror = ws_error;
193 channels[i].onerror = ws_error;
194 }
194 }
195 // switch from early-close to late-close message after 1s
195 // switch from early-close to late-close message after 1s
196 setTimeout(function() {
196 setTimeout(function() {
197 for (var i=0; i < channels.length; i++) {
197 for (var i=0; i < channels.length; i++) {
198 if (channels[i] !== null) {
198 if (channels[i] !== null) {
199 channels[i].onclose = ws_closed_late;
199 channels[i].onclose = ws_closed_late;
200 }
200 }
201 }
201 }
202 }, 1000);
202 }, 1000);
203 this.shell_channel.onmessage = $.proxy(this._handle_shell_reply, this);
203 this.shell_channel.onmessage = $.proxy(this._handle_shell_reply, this);
204 this.iopub_channel.onmessage = $.proxy(this._handle_iopub_message, this);
204 this.iopub_channel.onmessage = $.proxy(this._handle_iopub_message, this);
205 this.stdin_channel.onmessage = $.proxy(this._handle_input_request, this);
205 this.stdin_channel.onmessage = $.proxy(this._handle_input_request, this);
206 };
206 };
207
207
208 /**
208 /**
209 * Handle a websocket entering the open state
209 * Handle a websocket entering the open state
210 * sends session and cookie authentication info as first message.
210 * sends session and cookie authentication info as first message.
211 * Once all sockets are open, signal the Kernel.status_started event.
211 * Once all sockets are open, signal the Kernel.status_started event.
212 * @method _ws_opened
212 * @method _ws_opened
213 */
213 */
214 Kernel.prototype._ws_opened = function (evt) {
214 Kernel.prototype._ws_opened = function (evt) {
215 // send the session id so the Session object Python-side
215 // send the session id so the Session object Python-side
216 // has the same identity
216 // has the same identity
217 evt.target.send(this.session_id + ':' + document.cookie);
217 evt.target.send(this.session_id + ':' + document.cookie);
218
218
219 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
219 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
220 for (var i=0; i < channels.length; i++) {
220 for (var i=0; i < channels.length; i++) {
221 // if any channel is not ready, don't trigger event.
221 // if any channel is not ready, don't trigger event.
222 if ( channels[i].readyState == WebSocket.OPEN ) return;
222 if ( channels[i].readyState !== WebSocket.OPEN ) return;
223 }
223 }
224 // all events ready, trigger started event.
224 // all events ready, trigger started event.
225 this.events.trigger('status_started.Kernel', {kernel: this});
225 this.events.trigger('status_started.Kernel', {kernel: this});
226 };
226 };
227
227
228 /**
228 /**
229 * Stop the websocket channels.
229 * Stop the websocket channels.
230 * @method stop_channels
230 * @method stop_channels
231 */
231 */
232 Kernel.prototype.stop_channels = function () {
232 Kernel.prototype.stop_channels = function () {
233 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
233 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
234 for (var i=0; i < channels.length; i++) {
234 for (var i=0; i < channels.length; i++) {
235 if ( channels[i] !== null ) {
235 if ( channels[i] !== null ) {
236 channels[i].onclose = null;
236 channels[i].onclose = null;
237 channels[i].close();
237 channels[i].close();
238 }
238 }
239 }
239 }
240 this.shell_channel = this.iopub_channel = this.stdin_channel = null;
240 this.shell_channel = this.iopub_channel = this.stdin_channel = null;
241 };
241 };
242
242
243 // Main public methods.
243 // Main public methods.
244
244
245 // send a message on the Kernel's shell channel
245 // send a message on the Kernel's shell channel
246 Kernel.prototype.send_shell_message = function (msg_type, content, callbacks, metadata) {
246 Kernel.prototype.send_shell_message = function (msg_type, content, callbacks, metadata) {
247 var msg = this._get_msg(msg_type, content, metadata);
247 var msg = this._get_msg(msg_type, content, metadata);
248 this.shell_channel.send(JSON.stringify(msg));
248 this.shell_channel.send(JSON.stringify(msg));
249 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
249 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
250 return msg.header.msg_id;
250 return msg.header.msg_id;
251 };
251 };
252
252
253 /**
253 /**
254 * Get kernel info
254 * Get kernel info
255 *
255 *
256 * @param callback {function}
256 * @param callback {function}
257 * @method kernel_info
257 * @method kernel_info
258 *
258 *
259 * When calling this method, pass a callback function that expects one argument.
259 * When calling this method, pass a callback function that expects one argument.
260 * The callback will be passed the complete `kernel_info_reply` message documented
260 * The callback will be passed the complete `kernel_info_reply` message documented
261 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#kernel-info)
261 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#kernel-info)
262 */
262 */
263 Kernel.prototype.kernel_info = function (callback) {
263 Kernel.prototype.kernel_info = function (callback) {
264 var callbacks;
264 var callbacks;
265 if (callback) {
265 if (callback) {
266 callbacks = { shell : { reply : callback } };
266 callbacks = { shell : { reply : callback } };
267 }
267 }
268 return this.send_shell_message("kernel_info_request", {}, callbacks);
268 return this.send_shell_message("kernel_info_request", {}, callbacks);
269 };
269 };
270
270
271 /**
271 /**
272 * Get info on an object
272 * Get info on an object
273 *
273 *
274 * @param code {string}
274 * @param code {string}
275 * @param cursor_pos {integer}
275 * @param cursor_pos {integer}
276 * @param callback {function}
276 * @param callback {function}
277 * @method inspect
277 * @method inspect
278 *
278 *
279 * When calling this method, pass a callback function that expects one argument.
279 * When calling this method, pass a callback function that expects one argument.
280 * The callback will be passed the complete `inspect_reply` message documented
280 * The callback will be passed the complete `inspect_reply` message documented
281 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#object-information)
281 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#object-information)
282 */
282 */
283 Kernel.prototype.inspect = function (code, cursor_pos, callback) {
283 Kernel.prototype.inspect = function (code, cursor_pos, callback) {
284 var callbacks;
284 var callbacks;
285 if (callback) {
285 if (callback) {
286 callbacks = { shell : { reply : callback } };
286 callbacks = { shell : { reply : callback } };
287 }
287 }
288
288
289 var content = {
289 var content = {
290 code : code,
290 code : code,
291 cursor_pos : cursor_pos,
291 cursor_pos : cursor_pos,
292 detail_level : 0,
292 detail_level : 0,
293 };
293 };
294 return this.send_shell_message("inspect_request", content, callbacks);
294 return this.send_shell_message("inspect_request", content, callbacks);
295 };
295 };
296
296
297 /**
297 /**
298 * Execute given code into kernel, and pass result to callback.
298 * Execute given code into kernel, and pass result to callback.
299 *
299 *
300 * @async
300 * @async
301 * @method execute
301 * @method execute
302 * @param {string} code
302 * @param {string} code
303 * @param [callbacks] {Object} With the following keys (all optional)
303 * @param [callbacks] {Object} With the following keys (all optional)
304 * @param callbacks.shell.reply {function}
304 * @param callbacks.shell.reply {function}
305 * @param callbacks.shell.payload.[payload_name] {function}
305 * @param callbacks.shell.payload.[payload_name] {function}
306 * @param callbacks.iopub.output {function}
306 * @param callbacks.iopub.output {function}
307 * @param callbacks.iopub.clear_output {function}
307 * @param callbacks.iopub.clear_output {function}
308 * @param callbacks.input {function}
308 * @param callbacks.input {function}
309 * @param {object} [options]
309 * @param {object} [options]
310 * @param [options.silent=false] {Boolean}
310 * @param [options.silent=false] {Boolean}
311 * @param [options.user_expressions=empty_dict] {Dict}
311 * @param [options.user_expressions=empty_dict] {Dict}
312 * @param [options.allow_stdin=false] {Boolean} true|false
312 * @param [options.allow_stdin=false] {Boolean} true|false
313 *
313 *
314 * @example
314 * @example
315 *
315 *
316 * The options object should contain the options for the execute call. Its default
316 * The options object should contain the options for the execute call. Its default
317 * values are:
317 * values are:
318 *
318 *
319 * options = {
319 * options = {
320 * silent : true,
320 * silent : true,
321 * user_expressions : {},
321 * user_expressions : {},
322 * allow_stdin : false
322 * allow_stdin : false
323 * }
323 * }
324 *
324 *
325 * When calling this method pass a callbacks structure of the form:
325 * When calling this method pass a callbacks structure of the form:
326 *
326 *
327 * callbacks = {
327 * callbacks = {
328 * shell : {
328 * shell : {
329 * reply : execute_reply_callback,
329 * reply : execute_reply_callback,
330 * payload : {
330 * payload : {
331 * set_next_input : set_next_input_callback,
331 * set_next_input : set_next_input_callback,
332 * }
332 * }
333 * },
333 * },
334 * iopub : {
334 * iopub : {
335 * output : output_callback,
335 * output : output_callback,
336 * clear_output : clear_output_callback,
336 * clear_output : clear_output_callback,
337 * },
337 * },
338 * input : raw_input_callback
338 * input : raw_input_callback
339 * }
339 * }
340 *
340 *
341 * Each callback will be passed the entire message as a single arugment.
341 * Each callback will be passed the entire message as a single arugment.
342 * Payload handlers will be passed the corresponding payload and the execute_reply message.
342 * Payload handlers will be passed the corresponding payload and the execute_reply message.
343 */
343 */
344 Kernel.prototype.execute = function (code, callbacks, options) {
344 Kernel.prototype.execute = function (code, callbacks, options) {
345
345
346 var content = {
346 var content = {
347 code : code,
347 code : code,
348 silent : true,
348 silent : true,
349 store_history : false,
349 store_history : false,
350 user_expressions : {},
350 user_expressions : {},
351 allow_stdin : false
351 allow_stdin : false
352 };
352 };
353 callbacks = callbacks || {};
353 callbacks = callbacks || {};
354 if (callbacks.input !== undefined) {
354 if (callbacks.input !== undefined) {
355 content.allow_stdin = true;
355 content.allow_stdin = true;
356 }
356 }
357 $.extend(true, content, options);
357 $.extend(true, content, options);
358 this.events.trigger('execution_request.Kernel', {kernel: this, content:content});
358 this.events.trigger('execution_request.Kernel', {kernel: this, content:content});
359 return this.send_shell_message("execute_request", content, callbacks);
359 return this.send_shell_message("execute_request", content, callbacks);
360 };
360 };
361
361
362 /**
362 /**
363 * When calling this method, pass a function to be called with the `complete_reply` message
363 * When calling this method, pass a function to be called with the `complete_reply` message
364 * as its only argument when it arrives.
364 * as its only argument when it arrives.
365 *
365 *
366 * `complete_reply` is documented
366 * `complete_reply` is documented
367 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#complete)
367 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#complete)
368 *
368 *
369 * @method complete
369 * @method complete
370 * @param code {string}
370 * @param code {string}
371 * @param cursor_pos {integer}
371 * @param cursor_pos {integer}
372 * @param callback {function}
372 * @param callback {function}
373 *
373 *
374 */
374 */
375 Kernel.prototype.complete = function (code, cursor_pos, callback) {
375 Kernel.prototype.complete = function (code, cursor_pos, callback) {
376 var callbacks;
376 var callbacks;
377 if (callback) {
377 if (callback) {
378 callbacks = { shell : { reply : callback } };
378 callbacks = { shell : { reply : callback } };
379 }
379 }
380 var content = {
380 var content = {
381 code : code,
381 code : code,
382 cursor_pos : cursor_pos,
382 cursor_pos : cursor_pos,
383 };
383 };
384 return this.send_shell_message("complete_request", content, callbacks);
384 return this.send_shell_message("complete_request", content, callbacks);
385 };
385 };
386
386
387
387
388 Kernel.prototype.interrupt = function () {
388 Kernel.prototype.interrupt = function () {
389 if (this.running) {
389 if (this.running) {
390 this.events.trigger('status_interrupting.Kernel', {kernel: this});
390 this.events.trigger('status_interrupting.Kernel', {kernel: this});
391 this.post(utils.url_join_encode(this.kernel_url, "interrupt"));
391 this.post(utils.url_join_encode(this.kernel_url, "interrupt"));
392 }
392 }
393 };
393 };
394
394
395
395
396 Kernel.prototype.kill = function (success, error) {
396 Kernel.prototype.kill = function (success, error) {
397 if (this.running) {
397 if (this.running) {
398 this.running = false;
398 this.running = false;
399 var settings = {
399 var settings = {
400 cache : false,
400 cache : false,
401 type : "DELETE",
401 type : "DELETE",
402 success : success,
402 success : success,
403 error : error || utils.log_ajax_error,
403 error : error || utils.log_ajax_error,
404 };
404 };
405 $.ajax(utils.url_join_encode(this.kernel_url), settings);
405 $.ajax(utils.url_join_encode(this.kernel_url), settings);
406 this.stop_channels();
406 this.stop_channels();
407 }
407 }
408 };
408 };
409
409
410 Kernel.prototype.send_input_reply = function (input) {
410 Kernel.prototype.send_input_reply = function (input) {
411 var content = {
411 var content = {
412 value : input,
412 value : input,
413 };
413 };
414 this.events.trigger('input_reply.Kernel', {kernel: this, content:content});
414 this.events.trigger('input_reply.Kernel', {kernel: this, content:content});
415 var msg = this._get_msg("input_reply", content);
415 var msg = this._get_msg("input_reply", content);
416 this.stdin_channel.send(JSON.stringify(msg));
416 this.stdin_channel.send(JSON.stringify(msg));
417 return msg.header.msg_id;
417 return msg.header.msg_id;
418 };
418 };
419
419
420
420
421 // Reply handlers
421 // Reply handlers
422
422
423 Kernel.prototype.register_iopub_handler = function (msg_type, callback) {
423 Kernel.prototype.register_iopub_handler = function (msg_type, callback) {
424 this._iopub_handlers[msg_type] = callback;
424 this._iopub_handlers[msg_type] = callback;
425 };
425 };
426
426
427 Kernel.prototype.get_iopub_handler = function (msg_type) {
427 Kernel.prototype.get_iopub_handler = function (msg_type) {
428 // get iopub handler for a specific message type
428 // get iopub handler for a specific message type
429 return this._iopub_handlers[msg_type];
429 return this._iopub_handlers[msg_type];
430 };
430 };
431
431
432
432
433 Kernel.prototype.get_callbacks_for_msg = function (msg_id) {
433 Kernel.prototype.get_callbacks_for_msg = function (msg_id) {
434 // get callbacks for a specific message
434 // get callbacks for a specific message
435 if (msg_id == this.last_msg_id) {
435 if (msg_id == this.last_msg_id) {
436 return this.last_msg_callbacks;
436 return this.last_msg_callbacks;
437 } else {
437 } else {
438 return this._msg_callbacks[msg_id];
438 return this._msg_callbacks[msg_id];
439 }
439 }
440 };
440 };
441
441
442
442
443 Kernel.prototype.clear_callbacks_for_msg = function (msg_id) {
443 Kernel.prototype.clear_callbacks_for_msg = function (msg_id) {
444 if (this._msg_callbacks[msg_id] !== undefined ) {
444 if (this._msg_callbacks[msg_id] !== undefined ) {
445 delete this._msg_callbacks[msg_id];
445 delete this._msg_callbacks[msg_id];
446 }
446 }
447 };
447 };
448
448
449 Kernel.prototype._finish_shell = function (msg_id) {
449 Kernel.prototype._finish_shell = function (msg_id) {
450 var callbacks = this._msg_callbacks[msg_id];
450 var callbacks = this._msg_callbacks[msg_id];
451 if (callbacks !== undefined) {
451 if (callbacks !== undefined) {
452 callbacks.shell_done = true;
452 callbacks.shell_done = true;
453 if (callbacks.iopub_done) {
453 if (callbacks.iopub_done) {
454 this.clear_callbacks_for_msg(msg_id);
454 this.clear_callbacks_for_msg(msg_id);
455 }
455 }
456 }
456 }
457 };
457 };
458
458
459 Kernel.prototype._finish_iopub = function (msg_id) {
459 Kernel.prototype._finish_iopub = function (msg_id) {
460 var callbacks = this._msg_callbacks[msg_id];
460 var callbacks = this._msg_callbacks[msg_id];
461 if (callbacks !== undefined) {
461 if (callbacks !== undefined) {
462 callbacks.iopub_done = true;
462 callbacks.iopub_done = true;
463 if (callbacks.shell_done) {
463 if (callbacks.shell_done) {
464 this.clear_callbacks_for_msg(msg_id);
464 this.clear_callbacks_for_msg(msg_id);
465 }
465 }
466 }
466 }
467 };
467 };
468
468
469 /* Set callbacks for a particular message.
469 /* Set callbacks for a particular message.
470 * Callbacks should be a struct of the following form:
470 * Callbacks should be a struct of the following form:
471 * shell : {
471 * shell : {
472 *
472 *
473 * }
473 * }
474
474
475 */
475 */
476 Kernel.prototype.set_callbacks_for_msg = function (msg_id, callbacks) {
476 Kernel.prototype.set_callbacks_for_msg = function (msg_id, callbacks) {
477 this.last_msg_id = msg_id;
477 this.last_msg_id = msg_id;
478 if (callbacks) {
478 if (callbacks) {
479 // shallow-copy mapping, because we will modify it at the top level
479 // shallow-copy mapping, because we will modify it at the top level
480 var cbcopy = this._msg_callbacks[msg_id] = this.last_msg_callbacks = {};
480 var cbcopy = this._msg_callbacks[msg_id] = this.last_msg_callbacks = {};
481 cbcopy.shell = callbacks.shell;
481 cbcopy.shell = callbacks.shell;
482 cbcopy.iopub = callbacks.iopub;
482 cbcopy.iopub = callbacks.iopub;
483 cbcopy.input = callbacks.input;
483 cbcopy.input = callbacks.input;
484 cbcopy.shell_done = (!callbacks.shell);
484 cbcopy.shell_done = (!callbacks.shell);
485 cbcopy.iopub_done = (!callbacks.iopub);
485 cbcopy.iopub_done = (!callbacks.iopub);
486 } else {
486 } else {
487 this.last_msg_callbacks = {};
487 this.last_msg_callbacks = {};
488 }
488 }
489 };
489 };
490
490
491
491
492 Kernel.prototype._handle_shell_reply = function (e) {
492 Kernel.prototype._handle_shell_reply = function (e) {
493 var reply = $.parseJSON(e.data);
493 var reply = $.parseJSON(e.data);
494 this.events.trigger('shell_reply.Kernel', {kernel: this, reply:reply});
494 this.events.trigger('shell_reply.Kernel', {kernel: this, reply:reply});
495 var content = reply.content;
495 var content = reply.content;
496 var metadata = reply.metadata;
496 var metadata = reply.metadata;
497 var parent_id = reply.parent_header.msg_id;
497 var parent_id = reply.parent_header.msg_id;
498 var callbacks = this.get_callbacks_for_msg(parent_id);
498 var callbacks = this.get_callbacks_for_msg(parent_id);
499 if (!callbacks || !callbacks.shell) {
499 if (!callbacks || !callbacks.shell) {
500 return;
500 return;
501 }
501 }
502 var shell_callbacks = callbacks.shell;
502 var shell_callbacks = callbacks.shell;
503
503
504 // signal that shell callbacks are done
504 // signal that shell callbacks are done
505 this._finish_shell(parent_id);
505 this._finish_shell(parent_id);
506
506
507 if (shell_callbacks.reply !== undefined) {
507 if (shell_callbacks.reply !== undefined) {
508 shell_callbacks.reply(reply);
508 shell_callbacks.reply(reply);
509 }
509 }
510 if (content.payload && shell_callbacks.payload) {
510 if (content.payload && shell_callbacks.payload) {
511 this._handle_payloads(content.payload, shell_callbacks.payload, reply);
511 this._handle_payloads(content.payload, shell_callbacks.payload, reply);
512 }
512 }
513 };
513 };
514
514
515
515
516 Kernel.prototype._handle_payloads = function (payloads, payload_callbacks, msg) {
516 Kernel.prototype._handle_payloads = function (payloads, payload_callbacks, msg) {
517 var l = payloads.length;
517 var l = payloads.length;
518 // Payloads are handled by triggering events because we don't want the Kernel
518 // Payloads are handled by triggering events because we don't want the Kernel
519 // to depend on the Notebook or Pager classes.
519 // to depend on the Notebook or Pager classes.
520 for (var i=0; i<l; i++) {
520 for (var i=0; i<l; i++) {
521 var payload = payloads[i];
521 var payload = payloads[i];
522 var callback = payload_callbacks[payload.source];
522 var callback = payload_callbacks[payload.source];
523 if (callback) {
523 if (callback) {
524 callback(payload, msg);
524 callback(payload, msg);
525 }
525 }
526 }
526 }
527 };
527 };
528
528
529 Kernel.prototype._handle_status_message = function (msg) {
529 Kernel.prototype._handle_status_message = function (msg) {
530 var execution_state = msg.content.execution_state;
530 var execution_state = msg.content.execution_state;
531 var parent_id = msg.parent_header.msg_id;
531 var parent_id = msg.parent_header.msg_id;
532
532
533 // dispatch status msg callbacks, if any
533 // dispatch status msg callbacks, if any
534 var callbacks = this.get_callbacks_for_msg(parent_id);
534 var callbacks = this.get_callbacks_for_msg(parent_id);
535 if (callbacks && callbacks.iopub && callbacks.iopub.status) {
535 if (callbacks && callbacks.iopub && callbacks.iopub.status) {
536 try {
536 try {
537 callbacks.iopub.status(msg);
537 callbacks.iopub.status(msg);
538 } catch (e) {
538 } catch (e) {
539 console.log("Exception in status msg handler", e, e.stack);
539 console.log("Exception in status msg handler", e, e.stack);
540 }
540 }
541 }
541 }
542
542
543 if (execution_state === 'busy') {
543 if (execution_state === 'busy') {
544 this.events.trigger('status_busy.Kernel', {kernel: this});
544 this.events.trigger('status_busy.Kernel', {kernel: this});
545 } else if (execution_state === 'idle') {
545 } else if (execution_state === 'idle') {
546 // signal that iopub callbacks are (probably) done
546 // signal that iopub callbacks are (probably) done
547 // async output may still arrive,
547 // async output may still arrive,
548 // but only for the most recent request
548 // but only for the most recent request
549 this._finish_iopub(parent_id);
549 this._finish_iopub(parent_id);
550
550
551 // trigger status_idle event
551 // trigger status_idle event
552 this.events.trigger('status_idle.Kernel', {kernel: this});
552 this.events.trigger('status_idle.Kernel', {kernel: this});
553 } else if (execution_state === 'restarting') {
553 } else if (execution_state === 'restarting') {
554 // autorestarting is distinct from restarting,
554 // autorestarting is distinct from restarting,
555 // in that it means the kernel died and the server is restarting it.
555 // in that it means the kernel died and the server is restarting it.
556 // status_restarting sets the notification widget,
556 // status_restarting sets the notification widget,
557 // autorestart shows the more prominent dialog.
557 // autorestart shows the more prominent dialog.
558 this.events.trigger('status_autorestarting.Kernel', {kernel: this});
558 this.events.trigger('status_autorestarting.Kernel', {kernel: this});
559 this.events.trigger('status_restarting.Kernel', {kernel: this});
559 this.events.trigger('status_restarting.Kernel', {kernel: this});
560 } else if (execution_state === 'dead') {
560 } else if (execution_state === 'dead') {
561 this.stop_channels();
561 this.stop_channels();
562 this.events.trigger('status_dead.Kernel', {kernel: this});
562 this.events.trigger('status_dead.Kernel', {kernel: this});
563 }
563 }
564 };
564 };
565
565
566
566
567 // handle clear_output message
567 // handle clear_output message
568 Kernel.prototype._handle_clear_output = function (msg) {
568 Kernel.prototype._handle_clear_output = function (msg) {
569 var callbacks = this.get_callbacks_for_msg(msg.parent_header.msg_id);
569 var callbacks = this.get_callbacks_for_msg(msg.parent_header.msg_id);
570 if (!callbacks || !callbacks.iopub) {
570 if (!callbacks || !callbacks.iopub) {
571 return;
571 return;
572 }
572 }
573 var callback = callbacks.iopub.clear_output;
573 var callback = callbacks.iopub.clear_output;
574 if (callback) {
574 if (callback) {
575 callback(msg);
575 callback(msg);
576 }
576 }
577 };
577 };
578
578
579
579
580 // handle an output message (execute_result, display_data, etc.)
580 // handle an output message (execute_result, display_data, etc.)
581 Kernel.prototype._handle_output_message = function (msg) {
581 Kernel.prototype._handle_output_message = function (msg) {
582 var callbacks = this.get_callbacks_for_msg(msg.parent_header.msg_id);
582 var callbacks = this.get_callbacks_for_msg(msg.parent_header.msg_id);
583 if (!callbacks || !callbacks.iopub) {
583 if (!callbacks || !callbacks.iopub) {
584 return;
584 return;
585 }
585 }
586 var callback = callbacks.iopub.output;
586 var callback = callbacks.iopub.output;
587 if (callback) {
587 if (callback) {
588 callback(msg);
588 callback(msg);
589 }
589 }
590 };
590 };
591
591
592 // dispatch IOPub messages to respective handlers.
592 // dispatch IOPub messages to respective handlers.
593 // each message type should have a handler.
593 // each message type should have a handler.
594 Kernel.prototype._handle_iopub_message = function (e) {
594 Kernel.prototype._handle_iopub_message = function (e) {
595 var msg = $.parseJSON(e.data);
595 var msg = $.parseJSON(e.data);
596
596
597 var handler = this.get_iopub_handler(msg.header.msg_type);
597 var handler = this.get_iopub_handler(msg.header.msg_type);
598 if (handler !== undefined) {
598 if (handler !== undefined) {
599 handler(msg);
599 handler(msg);
600 }
600 }
601 };
601 };
602
602
603
603
604 Kernel.prototype._handle_input_request = function (e) {
604 Kernel.prototype._handle_input_request = function (e) {
605 var request = $.parseJSON(e.data);
605 var request = $.parseJSON(e.data);
606 var header = request.header;
606 var header = request.header;
607 var content = request.content;
607 var content = request.content;
608 var metadata = request.metadata;
608 var metadata = request.metadata;
609 var msg_type = header.msg_type;
609 var msg_type = header.msg_type;
610 if (msg_type !== 'input_request') {
610 if (msg_type !== 'input_request') {
611 console.log("Invalid input request!", request);
611 console.log("Invalid input request!", request);
612 return;
612 return;
613 }
613 }
614 var callbacks = this.get_callbacks_for_msg(request.parent_header.msg_id);
614 var callbacks = this.get_callbacks_for_msg(request.parent_header.msg_id);
615 if (callbacks) {
615 if (callbacks) {
616 if (callbacks.input) {
616 if (callbacks.input) {
617 callbacks.input(request);
617 callbacks.input(request);
618 }
618 }
619 }
619 }
620 };
620 };
621
621
622 // Backwards compatability.
622 // Backwards compatability.
623 IPython.Kernel = Kernel;
623 IPython.Kernel = Kernel;
624
624
625 return {'Kernel': Kernel};
625 return {'Kernel': Kernel};
626 });
626 });
General Comments 0
You need to be logged in to leave comments. Login now