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