##// END OF EJS Templates
Pass the header of output and clear_output messages to javascript callbacks
Jason Grout -
Show More
@@ -1,388 +1,389 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Kernel
9 // Kernel
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var utils = IPython.utils;
14 var utils = IPython.utils;
15
15
16 // Initialization and connection.
16 // Initialization and connection.
17
17
18 var Kernel = function (base_url) {
18 var Kernel = function (base_url) {
19 this.kernel_id = null;
19 this.kernel_id = null;
20 this.shell_channel = null;
20 this.shell_channel = null;
21 this.iopub_channel = null;
21 this.iopub_channel = null;
22 this.base_url = base_url;
22 this.base_url = base_url;
23 this.running = false;
23 this.running = false;
24 this.username = "username";
24 this.username = "username";
25 this.session_id = utils.uuid();
25 this.session_id = utils.uuid();
26 this._msg_callbacks = {};
26 this._msg_callbacks = {};
27
27
28 if (typeof(WebSocket) !== 'undefined') {
28 if (typeof(WebSocket) !== 'undefined') {
29 this.WebSocket = WebSocket;
29 this.WebSocket = WebSocket;
30 } else if (typeof(MozWebSocket) !== 'undefined') {
30 } else if (typeof(MozWebSocket) !== 'undefined') {
31 this.WebSocket = MozWebSocket;
31 this.WebSocket = MozWebSocket;
32 } else {
32 } else {
33 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.');
33 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.');
34 };
34 };
35 };
35 };
36
36
37
37
38 Kernel.prototype._get_msg = function (msg_type, content) {
38 Kernel.prototype._get_msg = function (msg_type, content) {
39 var msg = {
39 var msg = {
40 header : {
40 header : {
41 msg_id : utils.uuid(),
41 msg_id : utils.uuid(),
42 username : this.username,
42 username : this.username,
43 session : this.session_id,
43 session : this.session_id,
44 msg_type : msg_type
44 msg_type : msg_type
45 },
45 },
46 content : content,
46 content : content,
47 parent_header : {}
47 parent_header : {}
48 };
48 };
49 return msg;
49 return msg;
50 };
50 };
51
51
52 Kernel.prototype.start = function (notebook_id) {
52 Kernel.prototype.start = function (notebook_id) {
53 var that = this;
53 var that = this;
54 if (!this.running) {
54 if (!this.running) {
55 var qs = $.param({notebook:notebook_id});
55 var qs = $.param({notebook:notebook_id});
56 var url = this.base_url + '?' + qs;
56 var url = this.base_url + '?' + qs;
57 $.post(url,
57 $.post(url,
58 $.proxy(that._kernel_started,that),
58 $.proxy(that._kernel_started,that),
59 'json'
59 'json'
60 );
60 );
61 };
61 };
62 };
62 };
63
63
64
64
65 Kernel.prototype.restart = function () {
65 Kernel.prototype.restart = function () {
66 $([IPython.events]).trigger('status_restarting.Kernel');
66 $([IPython.events]).trigger('status_restarting.Kernel');
67 var that = this;
67 var that = this;
68 if (this.running) {
68 if (this.running) {
69 this.stop_channels();
69 this.stop_channels();
70 var url = this.kernel_url + "/restart";
70 var url = this.kernel_url + "/restart";
71 $.post(url,
71 $.post(url,
72 $.proxy(that._kernel_started, that),
72 $.proxy(that._kernel_started, that),
73 'json'
73 'json'
74 );
74 );
75 };
75 };
76 };
76 };
77
77
78
78
79 Kernel.prototype._kernel_started = function (json) {
79 Kernel.prototype._kernel_started = function (json) {
80 console.log("Kernel started: ", json.kernel_id);
80 console.log("Kernel started: ", json.kernel_id);
81 this.running = true;
81 this.running = true;
82 this.kernel_id = json.kernel_id;
82 this.kernel_id = json.kernel_id;
83 this.ws_url = json.ws_url;
83 this.ws_url = json.ws_url;
84 this.kernel_url = this.base_url + "/" + this.kernel_id;
84 this.kernel_url = this.base_url + "/" + this.kernel_id;
85 this.start_channels();
85 this.start_channels();
86 this.shell_channel.onmessage = $.proxy(this._handle_shell_reply,this);
86 this.shell_channel.onmessage = $.proxy(this._handle_shell_reply,this);
87 this.iopub_channel.onmessage = $.proxy(this._handle_iopub_reply,this);
87 this.iopub_channel.onmessage = $.proxy(this._handle_iopub_reply,this);
88 };
88 };
89
89
90
90
91 Kernel.prototype._websocket_closed = function(ws_url, early){
91 Kernel.prototype._websocket_closed = function(ws_url, early){
92 var msg;
92 var msg;
93 var parent_item = $('body');
93 var parent_item = $('body');
94 if (early) {
94 if (early) {
95 msg = "Websocket connection to " + ws_url + " could not be established." +
95 msg = "Websocket connection to " + ws_url + " could not be established." +
96 " You will NOT be able to run code." +
96 " You will NOT be able to run code." +
97 " Your browser may not be compatible with the websocket version in the server," +
97 " Your browser may not be compatible with the websocket version in the server," +
98 " or if the url does not look right, there could be an error in the" +
98 " or if the url does not look right, there could be an error in the" +
99 " server's configuration.";
99 " server's configuration.";
100 } else {
100 } else {
101 IPython.notification_widget.set_message('Reconnecting Websockets', 1000);
101 IPython.notification_widget.set_message('Reconnecting Websockets', 1000);
102 this.start_channels();
102 this.start_channels();
103 return;
103 return;
104 }
104 }
105 var dialog = $('<div/>');
105 var dialog = $('<div/>');
106 dialog.html(msg);
106 dialog.html(msg);
107 parent_item.append(dialog);
107 parent_item.append(dialog);
108 dialog.dialog({
108 dialog.dialog({
109 resizable: false,
109 resizable: false,
110 modal: true,
110 modal: true,
111 title: "Websocket closed",
111 title: "Websocket closed",
112 closeText: "",
112 closeText: "",
113 close: function(event, ui) {$(this).dialog('destroy').remove();},
113 close: function(event, ui) {$(this).dialog('destroy').remove();},
114 buttons : {
114 buttons : {
115 "OK": function () {
115 "OK": function () {
116 $(this).dialog('close');
116 $(this).dialog('close');
117 }
117 }
118 }
118 }
119 });
119 });
120
120
121 };
121 };
122
122
123 Kernel.prototype.start_channels = function () {
123 Kernel.prototype.start_channels = function () {
124 var that = this;
124 var that = this;
125 this.stop_channels();
125 this.stop_channels();
126 var ws_url = this.ws_url + this.kernel_url;
126 var ws_url = this.ws_url + this.kernel_url;
127 console.log("Starting WS:", ws_url);
127 console.log("Starting WS:", ws_url);
128 this.shell_channel = new this.WebSocket(ws_url + "/shell");
128 this.shell_channel = new this.WebSocket(ws_url + "/shell");
129 this.iopub_channel = new this.WebSocket(ws_url + "/iopub");
129 this.iopub_channel = new this.WebSocket(ws_url + "/iopub");
130 send_cookie = function(){
130 send_cookie = function(){
131 this.send(document.cookie);
131 this.send(document.cookie);
132 };
132 };
133 var already_called_onclose = false; // only alert once
133 var already_called_onclose = false; // only alert once
134 ws_closed_early = function(evt){
134 ws_closed_early = function(evt){
135 if (already_called_onclose){
135 if (already_called_onclose){
136 return;
136 return;
137 }
137 }
138 already_called_onclose = true;
138 already_called_onclose = true;
139 if ( ! evt.wasClean ){
139 if ( ! evt.wasClean ){
140 that._websocket_closed(ws_url, true);
140 that._websocket_closed(ws_url, true);
141 }
141 }
142 };
142 };
143 ws_closed_late = function(evt){
143 ws_closed_late = function(evt){
144 if (already_called_onclose){
144 if (already_called_onclose){
145 return;
145 return;
146 }
146 }
147 already_called_onclose = true;
147 already_called_onclose = true;
148 if ( ! evt.wasClean ){
148 if ( ! evt.wasClean ){
149 that._websocket_closed(ws_url, false);
149 that._websocket_closed(ws_url, false);
150 }
150 }
151 };
151 };
152 this.shell_channel.onopen = send_cookie;
152 this.shell_channel.onopen = send_cookie;
153 this.shell_channel.onclose = ws_closed_early;
153 this.shell_channel.onclose = ws_closed_early;
154 this.iopub_channel.onopen = send_cookie;
154 this.iopub_channel.onopen = send_cookie;
155 this.iopub_channel.onclose = ws_closed_early;
155 this.iopub_channel.onclose = ws_closed_early;
156 // switch from early-close to late-close message after 1s
156 // switch from early-close to late-close message after 1s
157 setTimeout(function(){
157 setTimeout(function(){
158 that.shell_channel.onclose = ws_closed_late;
158 that.shell_channel.onclose = ws_closed_late;
159 that.iopub_channel.onclose = ws_closed_late;
159 that.iopub_channel.onclose = ws_closed_late;
160 }, 1000);
160 }, 1000);
161 };
161 };
162
162
163
163
164 Kernel.prototype.stop_channels = function () {
164 Kernel.prototype.stop_channels = function () {
165 if (this.shell_channel !== null) {
165 if (this.shell_channel !== null) {
166 this.shell_channel.onclose = function (evt) {};
166 this.shell_channel.onclose = function (evt) {};
167 this.shell_channel.close();
167 this.shell_channel.close();
168 this.shell_channel = null;
168 this.shell_channel = null;
169 };
169 };
170 if (this.iopub_channel !== null) {
170 if (this.iopub_channel !== null) {
171 this.iopub_channel.onclose = function (evt) {};
171 this.iopub_channel.onclose = function (evt) {};
172 this.iopub_channel.close();
172 this.iopub_channel.close();
173 this.iopub_channel = null;
173 this.iopub_channel = null;
174 };
174 };
175 };
175 };
176
176
177 // Main public methods.
177 // Main public methods.
178
178
179 Kernel.prototype.object_info_request = function (objname, callbacks) {
179 Kernel.prototype.object_info_request = function (objname, callbacks) {
180 // When calling this method pass a callbacks structure of the form:
180 // When calling this method pass a callbacks structure of the form:
181 //
181 //
182 // callbacks = {
182 // callbacks = {
183 // 'object_info_reply': object_into_reply_callback
183 // 'object_info_reply': object_into_reply_callback
184 // }
184 // }
185 //
185 //
186 // The object_info_reply_callback will be passed the content object of the
186 // The object_info_reply_callback will be passed the content object of the
187 // object_into_reply message documented here:
187 // object_into_reply message documented here:
188 //
188 //
189 // http://ipython.org/ipython-doc/dev/development/messaging.html#object-information
189 // http://ipython.org/ipython-doc/dev/development/messaging.html#object-information
190 if(typeof(objname)!=null && objname!=null)
190 if(typeof(objname)!=null && objname!=null)
191 {
191 {
192 var content = {
192 var content = {
193 oname : objname.toString(),
193 oname : objname.toString(),
194 };
194 };
195 var msg = this._get_msg("object_info_request", content);
195 var msg = this._get_msg("object_info_request", content);
196 this.shell_channel.send(JSON.stringify(msg));
196 this.shell_channel.send(JSON.stringify(msg));
197 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
197 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
198 return msg.header.msg_id;
198 return msg.header.msg_id;
199 }
199 }
200 return;
200 return;
201 }
201 }
202
202
203 Kernel.prototype.execute = function (code, callbacks, options) {
203 Kernel.prototype.execute = function (code, callbacks, options) {
204 // The options object should contain the options for the execute call. Its default
204 // The options object should contain the options for the execute call. Its default
205 // values are:
205 // values are:
206 //
206 //
207 // options = {
207 // options = {
208 // silent : true,
208 // silent : true,
209 // user_variables : [],
209 // user_variables : [],
210 // user_expressions : {},
210 // user_expressions : {},
211 // allow_stdin : false
211 // allow_stdin : false
212 // }
212 // }
213 //
213 //
214 // When calling this method pass a callbacks structure of the form:
214 // When calling this method pass a callbacks structure of the form:
215 //
215 //
216 // callbacks = {
216 // callbacks = {
217 // 'execute_reply': execute_reply_callback,
217 // 'execute_reply': execute_reply_callback,
218 // 'output': output_callback,
218 // 'output': output_callback,
219 // 'clear_output': clear_output_callback,
219 // 'clear_output': clear_output_callback,
220 // 'set_next_input': set_next_input_callback
220 // 'set_next_input': set_next_input_callback
221 // }
221 // }
222 //
222 //
223 // The execute_reply_callback will be passed the content object of the execute_reply
223 // The execute_reply_callback will be passed the content object of the execute_reply
224 // message documented here:
224 // message documented here:
225 //
225 //
226 // http://ipython.org/ipython-doc/dev/development/messaging.html#execute
226 // http://ipython.org/ipython-doc/dev/development/messaging.html#execute
227 //
227 //
228 // The output_callback will be passed msg_type ('stream','display_data','pyout','pyerr')
228 // The output_callback will be passed msg_type ('stream','display_data','pyout','pyerr')
229 // of the output and the content object of the PUB/SUB channel that contains the
229 // of the output and the content and header objects of the PUB/SUB channel that contains the
230 // output:
230 // output:
231 //
231 //
232 // http://ipython.org/ipython-doc/dev/development/messaging.html#messages-on-the-pub-sub-socket
232 // http://ipython.org/ipython-doc/dev/development/messaging.html#messages-on-the-pub-sub-socket
233 //
233 //
234 // The clear_output_callback will be passed a content object that contains
234 // The clear_output_callback will be passed a content object that contains
235 // stdout, stderr and other fields that are booleans.
235 // stdout, stderr and other fields that are booleans, as well as the header object.
236 //
236 //
237 // The set_next_input_callback will bepassed the text that should become the next
237 // The set_next_input_callback will be passed the text that should become the next
238 // input cell.
238 // input cell.
239
239
240 var content = {
240 var content = {
241 code : code,
241 code : code,
242 silent : true,
242 silent : true,
243 user_variables : [],
243 user_variables : [],
244 user_expressions : {},
244 user_expressions : {},
245 allow_stdin : false
245 allow_stdin : false
246 };
246 };
247 $.extend(true, content, options)
247 $.extend(true, content, options)
248 var msg = this._get_msg("execute_request", content);
248 var msg = this._get_msg("execute_request", content);
249 this.shell_channel.send(JSON.stringify(msg));
249 this.shell_channel.send(JSON.stringify(msg));
250 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
250 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
251 return msg.header.msg_id;
251 return msg.header.msg_id;
252 };
252 };
253
253
254
254
255 Kernel.prototype.complete = function (line, cursor_pos, callbacks) {
255 Kernel.prototype.complete = function (line, cursor_pos, callbacks) {
256 // When calling this method pass a callbacks structure of the form:
256 // When calling this method pass a callbacks structure of the form:
257 //
257 //
258 // callbacks = {
258 // callbacks = {
259 // 'complete_reply': complete_reply_callback
259 // 'complete_reply': complete_reply_callback
260 // }
260 // }
261 //
261 //
262 // The complete_reply_callback will be passed the content object of the
262 // The complete_reply_callback will be passed the content object of the
263 // complete_reply message documented here:
263 // complete_reply message documented here:
264 //
264 //
265 // http://ipython.org/ipython-doc/dev/development/messaging.html#complete
265 // http://ipython.org/ipython-doc/dev/development/messaging.html#complete
266 callbacks = callbacks || {};
266 callbacks = callbacks || {};
267 var content = {
267 var content = {
268 text : '',
268 text : '',
269 line : line,
269 line : line,
270 cursor_pos : cursor_pos
270 cursor_pos : cursor_pos
271 };
271 };
272 var msg = this._get_msg("complete_request", content);
272 var msg = this._get_msg("complete_request", content);
273 this.shell_channel.send(JSON.stringify(msg));
273 this.shell_channel.send(JSON.stringify(msg));
274 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
274 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
275 return msg.header.msg_id;
275 return msg.header.msg_id;
276 };
276 };
277
277
278
278
279 Kernel.prototype.interrupt = function () {
279 Kernel.prototype.interrupt = function () {
280 if (this.running) {
280 if (this.running) {
281 $([IPython.events]).trigger('status_interrupting.Kernel');
281 $([IPython.events]).trigger('status_interrupting.Kernel');
282 $.post(this.kernel_url + "/interrupt");
282 $.post(this.kernel_url + "/interrupt");
283 };
283 };
284 };
284 };
285
285
286
286
287 Kernel.prototype.kill = function () {
287 Kernel.prototype.kill = function () {
288 if (this.running) {
288 if (this.running) {
289 this.running = false;
289 this.running = false;
290 var settings = {
290 var settings = {
291 cache : false,
291 cache : false,
292 type : "DELETE"
292 type : "DELETE"
293 };
293 };
294 $.ajax(this.kernel_url, settings);
294 $.ajax(this.kernel_url, settings);
295 };
295 };
296 };
296 };
297
297
298
298
299 // Reply handlers.
299 // Reply handlers.
300
300
301 Kernel.prototype.get_callbacks_for_msg = function (msg_id) {
301 Kernel.prototype.get_callbacks_for_msg = function (msg_id) {
302 var callbacks = this._msg_callbacks[msg_id];
302 var callbacks = this._msg_callbacks[msg_id];
303 return callbacks;
303 return callbacks;
304 };
304 };
305
305
306
306
307 Kernel.prototype.set_callbacks_for_msg = function (msg_id, callbacks) {
307 Kernel.prototype.set_callbacks_for_msg = function (msg_id, callbacks) {
308 this._msg_callbacks[msg_id] = callbacks || {};
308 this._msg_callbacks[msg_id] = callbacks || {};
309 }
309 }
310
310
311
311
312 Kernel.prototype._handle_shell_reply = function (e) {
312 Kernel.prototype._handle_shell_reply = function (e) {
313 reply = $.parseJSON(e.data);
313 reply = $.parseJSON(e.data);
314 var header = reply.header;
314 var header = reply.header;
315 var content = reply.content;
315 var content = reply.content;
316 var msg_type = header.msg_type;
316 var msg_type = header.msg_type;
317 var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
317 var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
318 if (callbacks !== undefined) {
318 if (callbacks !== undefined) {
319 var cb = callbacks[msg_type];
319 var cb = callbacks[msg_type];
320 if (cb !== undefined) {
320 if (cb !== undefined) {
321 cb(content);
321 cb(content);
322 }
322 }
323 };
323 };
324
324
325 if (content.payload !== undefined) {
325 if (content.payload !== undefined) {
326 var payload = content.payload || [];
326 var payload = content.payload || [];
327 this._handle_payload(callbacks, payload);
327 this._handle_payload(callbacks, payload);
328 }
328 }
329 };
329 };
330
330
331
331
332 Kernel.prototype._handle_payload = function (callbacks, payload) {
332 Kernel.prototype._handle_payload = function (callbacks, payload) {
333 var l = payload.length;
333 var l = payload.length;
334 // Payloads are handled by triggering events because we don't want the Kernel
334 // Payloads are handled by triggering events because we don't want the Kernel
335 // to depend on the Notebook or Pager classes.
335 // to depend on the Notebook or Pager classes.
336 for (var i=0; i<l; i++) {
336 for (var i=0; i<l; i++) {
337 if (payload[i].source === 'IPython.zmq.page.page') {
337 if (payload[i].source === 'IPython.zmq.page.page') {
338 var data = {'text':payload[i].text}
338 var data = {'text':payload[i].text}
339 $([IPython.events]).trigger('open_with_text.Pager', data);
339 $([IPython.events]).trigger('open_with_text.Pager', data);
340 } else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
340 } else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
341 if (callbacks.set_next_input !== undefined) {
341 if (callbacks.set_next_input !== undefined) {
342 callbacks.set_next_input(payload[i].text)
342 callbacks.set_next_input(payload[i].text)
343 }
343 }
344 }
344 }
345 };
345 };
346 };
346 };
347
347
348
348
349 Kernel.prototype._handle_iopub_reply = function (e) {
349 Kernel.prototype._handle_iopub_reply = function (e) {
350 reply = $.parseJSON(e.data);
350 reply = $.parseJSON(e.data);
351 var content = reply.content;
351 var content = reply.content;
352 var msg_type = reply.header.msg_type;
352 var msg_type = reply.header.msg_type;
353 var header = reply.header;
353 var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
354 var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
354 if (msg_type !== 'status' && callbacks === undefined) {
355 if (msg_type !== 'status' && callbacks === undefined) {
355 // Message not from one of this notebook's cells and there are no
356 // Message not from one of this notebook's cells and there are no
356 // callbacks to handle it.
357 // callbacks to handle it.
357 return;
358 return;
358 }
359 }
359 var output_types = ['stream','display_data','pyout','pyerr'];
360 var output_types = ['stream','display_data','pyout','pyerr'];
360 if (output_types.indexOf(msg_type) >= 0) {
361 if (output_types.indexOf(msg_type) >= 0) {
361 var cb = callbacks['output'];
362 var cb = callbacks['output'];
362 if (cb !== undefined) {
363 if (cb !== undefined) {
363 cb(msg_type, content);
364 cb(msg_type, content, header);
364 }
365 }
365 } else if (msg_type === 'status') {
366 } else if (msg_type === 'status') {
366 if (content.execution_state === 'busy') {
367 if (content.execution_state === 'busy') {
367 $([IPython.events]).trigger('status_busy.Kernel');
368 $([IPython.events]).trigger('status_busy.Kernel');
368 } else if (content.execution_state === 'idle') {
369 } else if (content.execution_state === 'idle') {
369 $([IPython.events]).trigger('status_idle.Kernel');
370 $([IPython.events]).trigger('status_idle.Kernel');
370 } else if (content.execution_state === 'dead') {
371 } else if (content.execution_state === 'dead') {
371 this.stop_channels();
372 this.stop_channels();
372 $([IPython.events]).trigger('status_dead.Kernel');
373 $([IPython.events]).trigger('status_dead.Kernel');
373 };
374 };
374 } else if (msg_type === 'clear_output') {
375 } else if (msg_type === 'clear_output') {
375 var cb = callbacks['clear_output'];
376 var cb = callbacks['clear_output'];
376 if (cb !== undefined) {
377 if (cb !== undefined) {
377 cb(content);
378 cb(content, header);
378 }
379 }
379 };
380 };
380 };
381 };
381
382
382
383
383 IPython.Kernel = Kernel;
384 IPython.Kernel = Kernel;
384
385
385 return IPython;
386 return IPython;
386
387
387 }(IPython));
388 }(IPython));
388
389
General Comments 0
You need to be logged in to leave comments. Login now