##// END OF EJS Templates
js comment about session id in first message
MinRK -
Show More
@@ -1,479 +1,481 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 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 * @submodule Kernel
16 16 */
17 17
18 18 var IPython = (function (IPython) {
19 19
20 20 var utils = IPython.utils;
21 21
22 22 // Initialization and connection.
23 23 /**
24 24 * A Kernel Class to communicate with the Python kernel
25 25 * @Class Kernel
26 26 */
27 27 var Kernel = function (base_url) {
28 28 this.kernel_id = null;
29 29 this.shell_channel = null;
30 30 this.iopub_channel = null;
31 31 this.stdin_channel = null;
32 32 this.base_url = base_url;
33 33 this.running = false;
34 34 this.username = "username";
35 35 this.session_id = utils.uuid();
36 36 this._msg_callbacks = {};
37 37
38 38 if (typeof(WebSocket) !== 'undefined') {
39 39 this.WebSocket = WebSocket;
40 40 } else if (typeof(MozWebSocket) !== 'undefined') {
41 41 this.WebSocket = MozWebSocket;
42 42 } else {
43 43 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.');
44 44 };
45 45 };
46 46
47 47
48 48 Kernel.prototype._get_msg = function (msg_type, content) {
49 49 var msg = {
50 50 header : {
51 51 msg_id : utils.uuid(),
52 52 username : this.username,
53 53 session : this.session_id,
54 54 msg_type : msg_type
55 55 },
56 56 metadata : {},
57 57 content : content,
58 58 parent_header : {}
59 59 };
60 60 return msg;
61 61 };
62 62
63 63 /**
64 64 * Start the Python kernel
65 65 * @method start
66 66 */
67 67 Kernel.prototype.start = function (notebook_id) {
68 68 var that = this;
69 69 if (!this.running) {
70 70 var qs = $.param({notebook:notebook_id});
71 71 var url = this.base_url + '?' + qs;
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 * Restart the python kernel.
81 81 *
82 82 * Emit a 'status_restarting.Kernel' event with
83 83 * the current object as parameter
84 84 *
85 85 * @method restart
86 86 */
87 87 Kernel.prototype.restart = function () {
88 88 $([IPython.events]).trigger('status_restarting.Kernel', {kernel: this});
89 89 var that = this;
90 90 if (this.running) {
91 91 this.stop_channels();
92 92 var url = this.kernel_url + "/restart";
93 93 $.post(url,
94 94 $.proxy(that._kernel_started, that),
95 95 'json'
96 96 );
97 97 };
98 98 };
99 99
100 100
101 101 Kernel.prototype._kernel_started = function (json) {
102 102 console.log("Kernel started: ", json.kernel_id);
103 103 this.running = true;
104 104 this.kernel_id = json.kernel_id;
105 105 this.ws_url = json.ws_url;
106 106 this.kernel_url = this.base_url + "/" + this.kernel_id;
107 107 this.start_channels();
108 108 $([IPython.events]).trigger('status_started.Kernel', {kernel: this});
109 109 };
110 110
111 111
112 112 Kernel.prototype._websocket_closed = function(ws_url, early) {
113 113 this.stop_channels();
114 114 $([IPython.events]).trigger('websocket_closed.Kernel',
115 115 {ws_url: ws_url, kernel: this, early: early}
116 116 );
117 117 };
118 118
119 119 /**
120 120 * Start the `shell`and `iopub` channels.
121 121 * Will stop and restart them if they already exist.
122 122 *
123 123 * @method start_channels
124 124 */
125 125 Kernel.prototype.start_channels = function () {
126 126 var that = this;
127 127 this.stop_channels();
128 128 var ws_url = this.ws_url + this.kernel_url;
129 129 console.log("Starting WebSockets:", ws_url);
130 130 this.shell_channel = new this.WebSocket(ws_url + "/shell");
131 131 this.stdin_channel = new this.WebSocket(ws_url + "/stdin");
132 132 this.iopub_channel = new this.WebSocket(ws_url + "/iopub");
133 133 send_cookie = function(){
134 // send the session id so the Session object Python-side
135 // has the same identity
134 136 this.send(that.session_id + ':' + document.cookie);
135 137 };
136 138 var already_called_onclose = false; // only alert once
137 139 var ws_closed_early = function(evt){
138 140 if (already_called_onclose){
139 141 return;
140 142 }
141 143 already_called_onclose = true;
142 144 if ( ! evt.wasClean ){
143 145 that._websocket_closed(ws_url, true);
144 146 }
145 147 };
146 148 var ws_closed_late = function(evt){
147 149 if (already_called_onclose){
148 150 return;
149 151 }
150 152 already_called_onclose = true;
151 153 if ( ! evt.wasClean ){
152 154 that._websocket_closed(ws_url, false);
153 155 }
154 156 };
155 157 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
156 158 for (var i=0; i < channels.length; i++) {
157 159 channels[i].onopen = send_cookie;
158 160 channels[i].onclose = ws_closed_early;
159 161 }
160 162 // switch from early-close to late-close message after 1s
161 163 setTimeout(function() {
162 164 for (var i=0; i < channels.length; i++) {
163 165 if (channels[i] !== null) {
164 166 channels[i].onclose = ws_closed_late;
165 167 }
166 168 }
167 169 }, 1000);
168 170 this.shell_channel.onmessage = $.proxy(this._handle_shell_reply, this);
169 171 this.iopub_channel.onmessage = $.proxy(this._handle_iopub_reply, this);
170 172 this.stdin_channel.onmessage = $.proxy(this._handle_input_request, this);
171 173
172 174 $([IPython.events]).on('send_input_reply.Kernel', function(evt, data) {
173 175 that.send_input_reply(data);
174 176 });
175 177 };
176 178
177 179 /**
178 180 * Start the `shell`and `iopub` channels.
179 181 * @method stop_channels
180 182 */
181 183 Kernel.prototype.stop_channels = function () {
182 184 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
183 185 for (var i=0; i < channels.length; i++) {
184 186 if ( channels[i] !== null ) {
185 187 channels[i].onclose = function (evt) {};
186 188 channels[i].close();
187 189 }
188 190 };
189 191 this.shell_channel = this.iopub_channel = this.stdin_channel = null;
190 192 };
191 193
192 194 // Main public methods.
193 195
194 196 /**
195 197 * Get info on object asynchronoulsy
196 198 *
197 199 * @async
198 200 * @param objname {string}
199 201 * @param callback {dict}
200 202 * @method object_info_request
201 203 *
202 204 * @example
203 205 *
204 206 * When calling this method pass a callbacks structure of the form:
205 207 *
206 208 * callbacks = {
207 209 * 'object_info_reply': object_info_reply_callback
208 210 * }
209 211 *
210 212 * The `object_info_reply_callback` will be passed the content object of the
211 213 *
212 214 * `object_into_reply` message documented in
213 215 * [IPython dev documentation](http://ipython.org/ipython-doc/dev/development/messaging.html#object-information)
214 216 */
215 217 Kernel.prototype.object_info_request = function (objname, callbacks) {
216 218 if(typeof(objname)!=null && objname!=null)
217 219 {
218 220 var content = {
219 221 oname : objname.toString(),
220 222 };
221 223 var msg = this._get_msg("object_info_request", content);
222 224 this.shell_channel.send(JSON.stringify(msg));
223 225 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
224 226 return msg.header.msg_id;
225 227 }
226 228 return;
227 229 }
228 230
229 231 /**
230 232 * Execute given code into kernel, and pass result to callback.
231 233 *
232 234 * @async
233 235 * @method execute
234 236 * @param {string} code
235 237 * @param callback {Object} With the following keys
236 238 * @param callback.'execute_reply' {function}
237 239 * @param callback.'output' {function}
238 240 * @param callback.'clear_output' {function}
239 241 * @param callback.'set_next_input' {function}
240 242 * @param {object} [options]
241 243 * @param [options.silent=false] {Boolean}
242 244 * @param [options.user_expressions=empty_dict] {Dict}
243 245 * @param [options.user_variables=empty_list] {List od Strings}
244 246 * @param [options.allow_stdin=false] {Boolean} true|false
245 247 *
246 248 * @example
247 249 *
248 250 * The options object should contain the options for the execute call. Its default
249 251 * values are:
250 252 *
251 253 * options = {
252 254 * silent : true,
253 255 * user_variables : [],
254 256 * user_expressions : {},
255 257 * allow_stdin : false
256 258 * }
257 259 *
258 260 * When calling this method pass a callbacks structure of the form:
259 261 *
260 262 * callbacks = {
261 263 * 'execute_reply': execute_reply_callback,
262 264 * 'output': output_callback,
263 265 * 'clear_output': clear_output_callback,
264 266 * 'set_next_input': set_next_input_callback
265 267 * }
266 268 *
267 269 * The `execute_reply_callback` will be passed the content and metadata
268 270 * objects of the `execute_reply` message documented
269 271 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#execute)
270 272 *
271 273 * The `output_callback` will be passed `msg_type` ('stream','display_data','pyout','pyerr')
272 274 * of the output and the content and metadata objects of the PUB/SUB channel that contains the
273 275 * output:
274 276 *
275 277 * http://ipython.org/ipython-doc/dev/development/messaging.html#messages-on-the-pub-sub-socket
276 278 *
277 279 * The `clear_output_callback` will be passed a content object that contains
278 280 * stdout, stderr and other fields that are booleans, as well as the metadata object.
279 281 *
280 282 * The `set_next_input_callback` will be passed the text that should become the next
281 283 * input cell.
282 284 */
283 285 Kernel.prototype.execute = function (code, callbacks, options) {
284 286
285 287 var content = {
286 288 code : code,
287 289 silent : true,
288 290 user_variables : [],
289 291 user_expressions : {},
290 292 allow_stdin : false
291 293 };
292 294 if (callbacks.input_request !== undefined) {
293 295 content.allow_stdin = true;
294 296 }
295 297 $.extend(true, content, options)
296 298 $([IPython.events]).trigger('execution_request.Kernel', {kernel: this, content:content});
297 299 var msg = this._get_msg("execute_request", content);
298 300 this.shell_channel.send(JSON.stringify(msg));
299 301 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
300 302 return msg.header.msg_id;
301 303 };
302 304
303 305 /**
304 306 * When calling this method pass a callbacks structure of the form:
305 307 *
306 308 * callbacks = {
307 309 * 'complete_reply': complete_reply_callback
308 310 * }
309 311 *
310 312 * The `complete_reply_callback` will be passed the content object of the
311 313 * `complete_reply` message documented
312 314 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#complete)
313 315 *
314 316 * @method complete
315 317 * @param line {integer}
316 318 * @param cursor_pos {integer}
317 319 * @param {dict} callbacks
318 320 * @param callbacks.complete_reply {function} `complete_reply_callback`
319 321 *
320 322 */
321 323 Kernel.prototype.complete = function (line, cursor_pos, callbacks) {
322 324 callbacks = callbacks || {};
323 325 var content = {
324 326 text : '',
325 327 line : line,
326 328 cursor_pos : cursor_pos
327 329 };
328 330 var msg = this._get_msg("complete_request", content);
329 331 this.shell_channel.send(JSON.stringify(msg));
330 332 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
331 333 return msg.header.msg_id;
332 334 };
333 335
334 336
335 337 Kernel.prototype.interrupt = function () {
336 338 if (this.running) {
337 339 $([IPython.events]).trigger('status_interrupting.Kernel', {kernel: this});
338 340 $.post(this.kernel_url + "/interrupt");
339 341 };
340 342 };
341 343
342 344
343 345 Kernel.prototype.kill = function () {
344 346 if (this.running) {
345 347 this.running = false;
346 348 var settings = {
347 349 cache : false,
348 350 type : "DELETE"
349 351 };
350 352 $.ajax(this.kernel_url, settings);
351 353 };
352 354 };
353 355
354 356 Kernel.prototype.send_input_reply = function (input) {
355 357 var content = {
356 358 value : input,
357 359 };
358 360 $([IPython.events]).trigger('input_reply.Kernel', {kernel: this, content:content});
359 361 var msg = this._get_msg("input_reply", content);
360 362 this.stdin_channel.send(JSON.stringify(msg));
361 363 return msg.header.msg_id;
362 364 };
363 365
364 366
365 367 // Reply handlers
366 368
367 369 Kernel.prototype.get_callbacks_for_msg = function (msg_id) {
368 370 var callbacks = this._msg_callbacks[msg_id];
369 371 return callbacks;
370 372 };
371 373
372 374
373 375 Kernel.prototype.set_callbacks_for_msg = function (msg_id, callbacks) {
374 376 this._msg_callbacks[msg_id] = callbacks || {};
375 377 }
376 378
377 379
378 380 Kernel.prototype._handle_shell_reply = function (e) {
379 381 var reply = $.parseJSON(e.data);
380 382 $([IPython.events]).trigger('shell_reply.Kernel', {kernel: this, reply:reply});
381 383 var header = reply.header;
382 384 var content = reply.content;
383 385 var metadata = reply.metadata;
384 386 var msg_type = header.msg_type;
385 387 var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
386 388 if (callbacks !== undefined) {
387 389 var cb = callbacks[msg_type];
388 390 if (cb !== undefined) {
389 391 cb(content, metadata);
390 392 }
391 393 };
392 394
393 395 if (content.payload !== undefined) {
394 396 var payload = content.payload || [];
395 397 this._handle_payload(callbacks, payload);
396 398 }
397 399 };
398 400
399 401
400 402 Kernel.prototype._handle_payload = function (callbacks, payload) {
401 403 var l = payload.length;
402 404 // Payloads are handled by triggering events because we don't want the Kernel
403 405 // to depend on the Notebook or Pager classes.
404 406 for (var i=0; i<l; i++) {
405 407 if (payload[i].source === 'IPython.kernel.zmq.page.page') {
406 408 var data = {'text':payload[i].text}
407 409 $([IPython.events]).trigger('open_with_text.Pager', data);
408 410 } else if (payload[i].source === 'IPython.kernel.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
409 411 if (callbacks.set_next_input !== undefined) {
410 412 callbacks.set_next_input(payload[i].text)
411 413 }
412 414 }
413 415 };
414 416 };
415 417
416 418
417 419 Kernel.prototype._handle_iopub_reply = function (e) {
418 420 var reply = $.parseJSON(e.data);
419 421 var content = reply.content;
420 422 var msg_type = reply.header.msg_type;
421 423 var metadata = reply.metadata;
422 424 var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
423 425 if (msg_type !== 'status' && callbacks === undefined) {
424 426 // Message not from one of this notebook's cells and there are no
425 427 // callbacks to handle it.
426 428 return;
427 429 }
428 430 var output_types = ['stream','display_data','pyout','pyerr'];
429 431 if (output_types.indexOf(msg_type) >= 0) {
430 432 var cb = callbacks['output'];
431 433 if (cb !== undefined) {
432 434 cb(msg_type, content, metadata);
433 435 }
434 436 } else if (msg_type === 'status') {
435 437 if (content.execution_state === 'busy') {
436 438 $([IPython.events]).trigger('status_busy.Kernel', {kernel: this});
437 439 } else if (content.execution_state === 'idle') {
438 440 $([IPython.events]).trigger('status_idle.Kernel', {kernel: this});
439 441 } else if (content.execution_state === 'restarting') {
440 442 $([IPython.events]).trigger('status_restarting.Kernel', {kernel: this});
441 443 } else if (content.execution_state === 'dead') {
442 444 this.stop_channels();
443 445 $([IPython.events]).trigger('status_dead.Kernel', {kernel: this});
444 446 };
445 447 } else if (msg_type === 'clear_output') {
446 448 var cb = callbacks['clear_output'];
447 449 if (cb !== undefined) {
448 450 cb(content, metadata);
449 451 }
450 452 };
451 453 };
452 454
453 455
454 456 Kernel.prototype._handle_input_request = function (e) {
455 457 var request = $.parseJSON(e.data);
456 458 var header = request.header;
457 459 var content = request.content;
458 460 var metadata = request.metadata;
459 461 var msg_type = header.msg_type;
460 462 if (msg_type !== 'input_request') {
461 463 console.log("Invalid input request!", request);
462 464 return;
463 465 }
464 466 var callbacks = this.get_callbacks_for_msg(request.parent_header.msg_id);
465 467 if (callbacks !== undefined) {
466 468 var cb = callbacks[msg_type];
467 469 if (cb !== undefined) {
468 470 cb(content, metadata);
469 471 }
470 472 };
471 473 };
472 474
473 475
474 476 IPython.Kernel = Kernel;
475 477
476 478 return IPython;
477 479
478 480 }(IPython));
479 481
General Comments 0
You need to be logged in to leave comments. Login now