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