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