##// END OF EJS Templates
Added support for multiple kernels.
Jonathan Frederic -
Show More
@@ -1,204 +1,205
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2013 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 // WidgetModel, WidgetView, and WidgetManager
10 10 //============================================================================
11 11 /**
12 12 * Base Widget classes
13 13 * @module IPython
14 14 * @namespace IPython
15 15 * @submodule widget
16 16 */
17 17
18 18 (function () {
19 19 "use strict";
20 20
21 21 // Use require.js 'define' method so that require.js is intelligent enough to
22 22 // syncronously load everything within this file when it is being 'required'
23 23 // elsewhere.
24 24 define(["underscore",
25 25 "backbone",
26 26 ], function (underscore, backbone) {
27 27
28 // Backbone.sync method must be in widgetmanager.js file instead of
29 // widget.js so it can be overwritten for different contexts.
30 28 Backbone.sync = function (method, model, options) {
29 // Sync widget models to back-end.
30 //
31 // Backbone.sync method must be in widgetmanager.js file instead of
32 // widget.js so it can be overwritten for different contexts.
31 33 var result = model._handle_sync(method, options);
32 34 if (options.success) {
33 35 options.success(result);
34 36 }
35 37 };
36 38
39
37 40 //--------------------------------------------------------------------
38 41 // WidgetManager class
39 42 //--------------------------------------------------------------------
40 var WidgetManager = function () {
41 this.comm_manager = null;
42 this._model_types = {}; /* Dictionary of model type names
43 (target_name) and model types. */
44 this._view_types = {}; /* Dictionary of view names and view types. */
45 this._models = {}; /* Dictionary of model ids and model instances */
46 };
47
43 var WidgetManager = function (comm_manager) {
44 // Public constructor
45 WidgetManager._managers.push(this);
48 46
49 WidgetManager.prototype.attach_comm_manager = function (comm_manager) {
47 // Attach a comm manager to the
50 48 this.comm_manager = comm_manager;
51 49
52 50 // Register already-registered widget model types with the comm manager.
53 for (var widget_model_name in this._model_types) {
54 // TODO: Should not be a for.
55 this.comm_manager.register_target(widget_model_name, $.proxy(this._handle_comm_open, this));
51 for (var name in WidgetManager._model_types) {
52 if (WidgetManager._model_types.hasOwnProperty(name)) {
53 this.comm_manager.register_target(name, $.proxy(this._handle_comm_open, this));
54
55 }
56 56 }
57 57 };
58 58
59 //--------------------------------------------------------------------
60 // Class level
61 //--------------------------------------------------------------------
62 WidgetManager._model_types = {}; /* Dictionary of model type names (target_name) and model types. */
63 WidgetManager._view_types = {}; /* Dictionary of view names and view types. */
64 WidgetManager._models = {}; /* Dictionary of model ids and model instances */
65 WidgetManager._managers = []; /* List of widget managers */
66
67 WidgetManager.register_widget_model = function (model_name, model_type) {
68 // Registers a widget model by name.
69 WidgetManager._model_types[model_name] = model_type;
59 70
60 WidgetManager.prototype.register_widget_model = function (widget_model_name, widget_model_type) {
61 71 // Register the widget with the comm manager. Make sure to pass this object's context
62 72 // in so `this` works in the call back.
63 if (this.comm_manager !== null) {
64 this.comm_manager.register_target(widget_model_name, $.proxy(this._handle_comm_open, this));
73 for (var i = 0; i < WidgetManager._managers.length; i++) {
74 var instance = WidgetManager._managers[i];
75 if (instance.comm_manager !== null) {
76 instance.comm_manager.register_target(model_name, $.proxy(instance._handle_comm_open, instance));
77 }
65 78 }
66 this._model_types[widget_model_name] = widget_model_type;
67 79 };
68 80
69
70 WidgetManager.prototype.register_widget_view = function (widget_view_name, widget_view_type) {
71 this._view_types[widget_view_name] = widget_view_type;
81 WidgetManager.register_widget_view = function (view_name, view_type) {
82 // Registers a widget view by name.
83 WidgetManager._view_types[view_name] = view_type;
72 84 };
73 85
74
86 //--------------------------------------------------------------------
87 // Instance level
88 //--------------------------------------------------------------------
75 89 WidgetManager.prototype.display_view = function(msg, model) {
76 90 var cell = this.get_msg_cell(msg.parent_header.msg_id);
77 91 if (cell === null) {
78 92 console.log("Could not determine where the display" +
79 93 " message was from. Widget will not be displayed");
80 94 } else {
81 95 var view = this.create_view(model, {cell: cell});
82 96 if (view === undefined) {
83 97 console.error("View creation failed", model);
84 98 }
85 99 if (cell.widget_subarea !== undefined
86 100 && cell.widget_subarea !== null) {
87 101
88 102 cell.widget_area.show();
89 103 cell.widget_subarea.append(view.$el);
90 104 }
91 105 }
92 106 },
93 107
94
95 108 WidgetManager.prototype.create_view = function(model, options) {
96 109 var view_name = model.get('view_name');
97 var ViewType = this._view_types[view_name];
110 var ViewType = WidgetManager._view_types[view_name];
98 111 if (ViewType !== undefined && ViewType !== null) {
99 112 var parameters = {model: model, options: options};
100 113 var view = new ViewType(parameters);
101 114 view.render();
102 115 IPython.keyboard_manager.register_events(view.$el);
103 116 model.views.push(view);
104 117 model.on('destroy', view.remove, view);
105 118 return view;
106 119 }
107 120 },
108 121
109
110 122 WidgetManager.prototype.get_msg_cell = function (msg_id) {
111 123 var cell = null;
112 124 // First, check to see if the msg was triggered by cell execution.
113 125 if (IPython.notebook !== undefined && IPython.notebook !== null) {
114 126 cell = IPython.notebook.get_msg_cell(msg_id);
115 127 }
116 128 if (cell !== null) {
117 129 return cell
118 130 }
119 131 // Second, check to see if a get_cell callback was defined
120 132 // for the message. get_cell callbacks are registered for
121 133 // widget messages, so this block is actually checking to see if the
122 134 // message was triggered by a widget.
123 var kernel = null;
124 if (this.comm_manager !== null) {
125 kernel = this.comm_manager.kernel;
126 }
135 var kernel = this.comm_manager.kernel;
127 136 if (kernel !== undefined && kernel !== null) {
128 137 var callbacks = kernel.get_callbacks_for_msg(msg_id);
129 138 if (callbacks !== undefined &&
130 139 callbacks.iopub !== undefined &&
131 140 callbacks.iopub.get_cell !== undefined) {
132 141
133 142 return callbacks.iopub.get_cell();
134 143 }
135 144 }
136 145
137 146 // Not triggered by a cell or widget (no get_cell callback
138 147 // exists).
139 148 return null;
140 149 };
141 150
142 151 WidgetManager.prototype.callbacks = function (view) {
143 152 // callback handlers specific a view
144 153 var callbacks = {};
145 154 var cell = view.options.cell;
146 155 if (cell !== null) {
147 156 // Try to get output handlers
148 157 var handle_output = null;
149 158 var handle_clear_output = null;
150 159 if (cell.output_area !== undefined && cell.output_area !== null) {
151 160 handle_output = $.proxy(cell.output_area.handle_output, cell.output_area);
152 161 handle_clear_output = $.proxy(cell.output_area.handle_clear_output, cell.output_area);
153 162 }
154 163
155 164 // Create callback dict using what is known
156 165 var that = this;
157 166 callbacks = {
158 167 iopub : {
159 168 output : handle_output,
160 169 clear_output : handle_clear_output,
161 170
162 171 status : function (msg) {
163 172 view.model._handle_status(msg, that.callbacks(view));
164 173 },
165 174
166 175 // Special function only registered by widget messages.
167 176 // Allows us to get the cell for a message so we know
168 177 // where to add widgets if the code requires it.
169 178 get_cell : function () {
170 179 return cell;
171 180 },
172 181 },
173 182 };
174 183 }
175 184 return callbacks;
176 185 };
177 186
178
179 187 WidgetManager.prototype.get_model = function (model_id) {
180 var model = this._models[model_id];
188 var model = WidgetManager._models[model_id];
181 189 if (model !== undefined && model.id == model_id) {
182 190 return model;
183 191 }
184 192 return null;
185 193 };
186 194
187
188 195 WidgetManager.prototype._handle_comm_open = function (comm, msg) {
196 var model_id = comm.comm_id;
189 197 var widget_type_name = msg.content.target_name;
190 var widget_model = new this._model_types[widget_type_name](this, comm.comm_id, comm);
191 this._models[comm.comm_id] = widget_model; // comm_id == model_id
198 var widget_model = new WidgetManager._model_types[widget_type_name](this, model_id, comm);
199 WidgetManager._models[model_id] = widget_model;
192 200 };
193 201
194 //--------------------------------------------------------------------
195 // Init code
196 //--------------------------------------------------------------------
197 202 IPython.WidgetManager = WidgetManager;
198 if (IPython.widget_manager === undefined || IPython.widget_manager === null) {
199 IPython.widget_manager = new WidgetManager();
200 }
201
202 return IPython.widget_manager;
203 return IPython.WidgetManager;
203 204 });
204 205 }());
@@ -1,587 +1,585
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 "use strict";
20 20
21 21 var utils = IPython.utils;
22 22
23 23 // Initialization and connection.
24 24 /**
25 25 * A Kernel Class to communicate with the Python kernel
26 26 * @Class Kernel
27 27 */
28 28 var Kernel = function (base_url) {
29 29 this.kernel_id = null;
30 30 this.shell_channel = null;
31 31 this.iopub_channel = null;
32 32 this.stdin_channel = null;
33 33 this.base_url = base_url;
34 34 this.running = false;
35 35 this.username = "username";
36 36 this.session_id = utils.uuid();
37 37 this._msg_callbacks = {};
38 38
39 39 if (typeof(WebSocket) !== 'undefined') {
40 40 this.WebSocket = WebSocket;
41 41 } else if (typeof(MozWebSocket) !== 'undefined') {
42 42 this.WebSocket = MozWebSocket;
43 43 } else {
44 44 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.');
45 45 }
46 46
47 47 this.bind_events();
48 48 this.init_iopub_handlers();
49 49 this.comm_manager = new IPython.CommManager(this);
50 // TODO: make the comm manager an arg to the widget manager initialization
51 this.widget_manager = new IPython.WidgetManager();
52 this.widget_manager.attach_comm_manager(this.comm_manager);
50 this.widget_manager = new IPython.WidgetManager(this.comm_manager);
53 51 };
54 52
55 53
56 54 Kernel.prototype._get_msg = function (msg_type, content, metadata) {
57 55 var msg = {
58 56 header : {
59 57 msg_id : utils.uuid(),
60 58 username : this.username,
61 59 session : this.session_id,
62 60 msg_type : msg_type
63 61 },
64 62 metadata : metadata || {},
65 63 content : content,
66 64 parent_header : {}
67 65 };
68 66 return msg;
69 67 };
70 68
71 69 Kernel.prototype.bind_events = function () {
72 70 var that = this;
73 71 $([IPython.events]).on('send_input_reply.Kernel', function(evt, data) {
74 72 that.send_input_reply(data);
75 73 });
76 74 };
77 75
78 76 // Initialize the iopub handlers
79 77
80 78 Kernel.prototype.init_iopub_handlers = function () {
81 79 var output_types = ['stream', 'display_data', 'pyout', 'pyerr'];
82 80 this._iopub_handlers = {};
83 81 this.register_iopub_handler('status', $.proxy(this._handle_status_message, this));
84 82 this.register_iopub_handler('clear_output', $.proxy(this._handle_clear_output, this));
85 83
86 84 for (var i=0; i < output_types.length; i++) {
87 85 this.register_iopub_handler(output_types[i], $.proxy(this._handle_output_message, this));
88 86 }
89 87 };
90 88
91 89 /**
92 90 * Start the Python kernel
93 91 * @method start
94 92 */
95 93 Kernel.prototype.start = function (params) {
96 94 params = params || {};
97 95 if (!this.running) {
98 96 var qs = $.param(params);
99 97 var url = this.base_url + '?' + qs;
100 98 $.post(url,
101 99 $.proxy(this._kernel_started, this),
102 100 'json'
103 101 );
104 102 }
105 103 };
106 104
107 105 /**
108 106 * Restart the python kernel.
109 107 *
110 108 * Emit a 'status_restarting.Kernel' event with
111 109 * the current object as parameter
112 110 *
113 111 * @method restart
114 112 */
115 113 Kernel.prototype.restart = function () {
116 114 $([IPython.events]).trigger('status_restarting.Kernel', {kernel: this});
117 115 if (this.running) {
118 116 this.stop_channels();
119 117 var url = utils.url_join_encode(this.kernel_url, "restart");
120 118 $.post(url,
121 119 $.proxy(this._kernel_started, this),
122 120 'json'
123 121 );
124 122 }
125 123 };
126 124
127 125
128 126 Kernel.prototype._kernel_started = function (json) {
129 127 console.log("Kernel started: ", json.id);
130 128 this.running = true;
131 129 this.kernel_id = json.id;
132 130 var ws_url = json.ws_url;
133 131 if (ws_url.match(/wss?:\/\//) === null) {
134 132 // trailing 's' in https will become wss for secure web sockets
135 133 var prot = location.protocol.replace('http', 'ws') + "//";
136 134 ws_url = prot + location.host + ws_url;
137 135 }
138 136 this.ws_url = ws_url;
139 137 this.kernel_url = utils.url_join_encode(this.base_url, this.kernel_id);
140 138 this.start_channels();
141 139 };
142 140
143 141
144 142 Kernel.prototype._websocket_closed = function(ws_url, early) {
145 143 this.stop_channels();
146 144 $([IPython.events]).trigger('websocket_closed.Kernel',
147 145 {ws_url: ws_url, kernel: this, early: early}
148 146 );
149 147 };
150 148
151 149 /**
152 150 * Start the `shell`and `iopub` channels.
153 151 * Will stop and restart them if they already exist.
154 152 *
155 153 * @method start_channels
156 154 */
157 155 Kernel.prototype.start_channels = function () {
158 156 var that = this;
159 157 this.stop_channels();
160 158 var ws_url = this.ws_url + this.kernel_url;
161 159 console.log("Starting WebSockets:", ws_url);
162 160 this.shell_channel = new this.WebSocket(ws_url + "/shell");
163 161 this.stdin_channel = new this.WebSocket(ws_url + "/stdin");
164 162 this.iopub_channel = new this.WebSocket(ws_url + "/iopub");
165 163
166 164 var already_called_onclose = false; // only alert once
167 165 var ws_closed_early = function(evt){
168 166 if (already_called_onclose){
169 167 return;
170 168 }
171 169 already_called_onclose = true;
172 170 if ( ! evt.wasClean ){
173 171 that._websocket_closed(ws_url, true);
174 172 }
175 173 };
176 174 var ws_closed_late = function(evt){
177 175 if (already_called_onclose){
178 176 return;
179 177 }
180 178 already_called_onclose = true;
181 179 if ( ! evt.wasClean ){
182 180 that._websocket_closed(ws_url, false);
183 181 }
184 182 };
185 183 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
186 184 for (var i=0; i < channels.length; i++) {
187 185 channels[i].onopen = $.proxy(this._ws_opened, this);
188 186 channels[i].onclose = ws_closed_early;
189 187 }
190 188 // switch from early-close to late-close message after 1s
191 189 setTimeout(function() {
192 190 for (var i=0; i < channels.length; i++) {
193 191 if (channels[i] !== null) {
194 192 channels[i].onclose = ws_closed_late;
195 193 }
196 194 }
197 195 }, 1000);
198 196 this.shell_channel.onmessage = $.proxy(this._handle_shell_reply, this);
199 197 this.iopub_channel.onmessage = $.proxy(this._handle_iopub_message, this);
200 198 this.stdin_channel.onmessage = $.proxy(this._handle_input_request, this);
201 199 };
202 200
203 201 /**
204 202 * Handle a websocket entering the open state
205 203 * sends session and cookie authentication info as first message.
206 204 * Once all sockets are open, signal the Kernel.status_started event.
207 205 * @method _ws_opened
208 206 */
209 207 Kernel.prototype._ws_opened = function (evt) {
210 208 // send the session id so the Session object Python-side
211 209 // has the same identity
212 210 evt.target.send(this.session_id + ':' + document.cookie);
213 211
214 212 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
215 213 for (var i=0; i < channels.length; i++) {
216 214 // if any channel is not ready, don't trigger event.
217 215 if ( !channels[i].readyState ) return;
218 216 }
219 217 // all events ready, trigger started event.
220 218 $([IPython.events]).trigger('status_started.Kernel', {kernel: this});
221 219 };
222 220
223 221 /**
224 222 * Stop the websocket channels.
225 223 * @method stop_channels
226 224 */
227 225 Kernel.prototype.stop_channels = function () {
228 226 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
229 227 for (var i=0; i < channels.length; i++) {
230 228 if ( channels[i] !== null ) {
231 229 channels[i].onclose = null;
232 230 channels[i].close();
233 231 }
234 232 }
235 233 this.shell_channel = this.iopub_channel = this.stdin_channel = null;
236 234 };
237 235
238 236 // Main public methods.
239 237
240 238 // send a message on the Kernel's shell channel
241 239 Kernel.prototype.send_shell_message = function (msg_type, content, callbacks, metadata) {
242 240 var msg = this._get_msg(msg_type, content, metadata);
243 241 this.shell_channel.send(JSON.stringify(msg));
244 242 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
245 243 return msg.header.msg_id;
246 244 };
247 245
248 246 /**
249 247 * Get info on an object
250 248 *
251 249 * @param objname {string}
252 250 * @param callback {function}
253 251 * @method object_info
254 252 *
255 253 * When calling this method, pass a callback function that expects one argument.
256 254 * The callback will be passed the complete `object_info_reply` message documented
257 255 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#object-information)
258 256 */
259 257 Kernel.prototype.object_info = function (objname, callback) {
260 258 var callbacks;
261 259 if (callback) {
262 260 callbacks = { shell : { reply : callback } };
263 261 }
264 262
265 263 if (typeof(objname) !== null && objname !== null) {
266 264 var content = {
267 265 oname : objname.toString(),
268 266 detail_level : 0,
269 267 };
270 268 return this.send_shell_message("object_info_request", content, callbacks);
271 269 }
272 270 return;
273 271 };
274 272
275 273 /**
276 274 * Execute given code into kernel, and pass result to callback.
277 275 *
278 276 * @async
279 277 * @method execute
280 278 * @param {string} code
281 279 * @param [callbacks] {Object} With the following keys (all optional)
282 280 * @param callbacks.shell.reply {function}
283 281 * @param callbacks.shell.payload.[payload_name] {function}
284 282 * @param callbacks.iopub.output {function}
285 283 * @param callbacks.iopub.clear_output {function}
286 284 * @param callbacks.input {function}
287 285 * @param {object} [options]
288 286 * @param [options.silent=false] {Boolean}
289 287 * @param [options.user_expressions=empty_dict] {Dict}
290 288 * @param [options.user_variables=empty_list] {List od Strings}
291 289 * @param [options.allow_stdin=false] {Boolean} true|false
292 290 *
293 291 * @example
294 292 *
295 293 * The options object should contain the options for the execute call. Its default
296 294 * values are:
297 295 *
298 296 * options = {
299 297 * silent : true,
300 298 * user_variables : [],
301 299 * user_expressions : {},
302 300 * allow_stdin : false
303 301 * }
304 302 *
305 303 * When calling this method pass a callbacks structure of the form:
306 304 *
307 305 * callbacks = {
308 306 * shell : {
309 307 * reply : execute_reply_callback,
310 308 * payload : {
311 309 * set_next_input : set_next_input_callback,
312 310 * }
313 311 * },
314 312 * iopub : {
315 313 * output : output_callback,
316 314 * clear_output : clear_output_callback,
317 315 * },
318 316 * input : raw_input_callback
319 317 * }
320 318 *
321 319 * Each callback will be passed the entire message as a single arugment.
322 320 * Payload handlers will be passed the corresponding payload and the execute_reply message.
323 321 */
324 322 Kernel.prototype.execute = function (code, callbacks, options) {
325 323
326 324 var content = {
327 325 code : code,
328 326 silent : true,
329 327 store_history : false,
330 328 user_variables : [],
331 329 user_expressions : {},
332 330 allow_stdin : false
333 331 };
334 332 callbacks = callbacks || {};
335 333 if (callbacks.input !== undefined) {
336 334 content.allow_stdin = true;
337 335 }
338 336 $.extend(true, content, options);
339 337 $([IPython.events]).trigger('execution_request.Kernel', {kernel: this, content:content});
340 338 return this.send_shell_message("execute_request", content, callbacks);
341 339 };
342 340
343 341 /**
344 342 * When calling this method, pass a function to be called with the `complete_reply` message
345 343 * as its only argument when it arrives.
346 344 *
347 345 * `complete_reply` is documented
348 346 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#complete)
349 347 *
350 348 * @method complete
351 349 * @param line {integer}
352 350 * @param cursor_pos {integer}
353 351 * @param callback {function}
354 352 *
355 353 */
356 354 Kernel.prototype.complete = function (line, cursor_pos, callback) {
357 355 var callbacks;
358 356 if (callback) {
359 357 callbacks = { shell : { reply : callback } };
360 358 }
361 359 var content = {
362 360 text : '',
363 361 line : line,
364 362 block : null,
365 363 cursor_pos : cursor_pos
366 364 };
367 365 return this.send_shell_message("complete_request", content, callbacks);
368 366 };
369 367
370 368
371 369 Kernel.prototype.interrupt = function () {
372 370 if (this.running) {
373 371 $([IPython.events]).trigger('status_interrupting.Kernel', {kernel: this});
374 372 $.post(this.kernel_url + "/interrupt");
375 373 }
376 374 };
377 375
378 376
379 377 Kernel.prototype.kill = function () {
380 378 if (this.running) {
381 379 this.running = false;
382 380 var settings = {
383 381 cache : false,
384 382 type : "DELETE"
385 383 };
386 384 $.ajax(this.kernel_url, settings);
387 385 }
388 386 };
389 387
390 388 Kernel.prototype.send_input_reply = function (input) {
391 389 var content = {
392 390 value : input,
393 391 };
394 392 $([IPython.events]).trigger('input_reply.Kernel', {kernel: this, content:content});
395 393 var msg = this._get_msg("input_reply", content);
396 394 this.stdin_channel.send(JSON.stringify(msg));
397 395 return msg.header.msg_id;
398 396 };
399 397
400 398
401 399 // Reply handlers
402 400
403 401 Kernel.prototype.register_iopub_handler = function (msg_type, callback) {
404 402 this._iopub_handlers[msg_type] = callback;
405 403 };
406 404
407 405 Kernel.prototype.get_iopub_handler = function (msg_type) {
408 406 // get iopub handler for a specific message type
409 407 return this._iopub_handlers[msg_type];
410 408 };
411 409
412 410
413 411 Kernel.prototype.get_callbacks_for_msg = function (msg_id) {
414 412 // get callbacks for a specific message
415 413 return this._msg_callbacks[msg_id];
416 414 };
417 415
418 416
419 417 Kernel.prototype.clear_callbacks_for_msg = function (msg_id) {
420 418 if (this._msg_callbacks[msg_id] !== undefined ) {
421 419 delete this._msg_callbacks[msg_id];
422 420 }
423 421 };
424 422
425 423 /* Set callbacks for a particular message.
426 424 * Callbacks should be a struct of the following form:
427 425 * shell : {
428 426 *
429 427 * }
430 428
431 429 */
432 430 Kernel.prototype.set_callbacks_for_msg = function (msg_id, callbacks) {
433 431 if (callbacks) {
434 432 // shallow-copy mapping, because we will modify it at the top level
435 433 var cbcopy = this._msg_callbacks[msg_id] = {};
436 434 cbcopy.shell = callbacks.shell;
437 435 cbcopy.iopub = callbacks.iopub;
438 436 cbcopy.input = callbacks.input;
439 437 this._msg_callbacks[msg_id] = cbcopy;
440 438 }
441 439 };
442 440
443 441
444 442 Kernel.prototype._handle_shell_reply = function (e) {
445 443 var reply = $.parseJSON(e.data);
446 444 $([IPython.events]).trigger('shell_reply.Kernel', {kernel: this, reply:reply});
447 445 var content = reply.content;
448 446 var metadata = reply.metadata;
449 447 var parent_id = reply.parent_header.msg_id;
450 448 var callbacks = this.get_callbacks_for_msg(parent_id);
451 449 if (!callbacks || !callbacks.shell) {
452 450 return;
453 451 }
454 452 var shell_callbacks = callbacks.shell;
455 453
456 454 // clear callbacks on shell
457 455 delete callbacks.shell;
458 456 delete callbacks.input;
459 457 if (!callbacks.iopub) {
460 458 this.clear_callbacks_for_msg(parent_id);
461 459 }
462 460
463 461 if (shell_callbacks.reply !== undefined) {
464 462 shell_callbacks.reply(reply);
465 463 }
466 464 if (content.payload && shell_callbacks.payload) {
467 465 this._handle_payloads(content.payload, shell_callbacks.payload, reply);
468 466 }
469 467 };
470 468
471 469
472 470 Kernel.prototype._handle_payloads = function (payloads, payload_callbacks, msg) {
473 471 var l = payloads.length;
474 472 // Payloads are handled by triggering events because we don't want the Kernel
475 473 // to depend on the Notebook or Pager classes.
476 474 for (var i=0; i<l; i++) {
477 475 var payload = payloads[i];
478 476 var callback = payload_callbacks[payload.source];
479 477 if (callback) {
480 478 callback(payload, msg);
481 479 }
482 480 }
483 481 };
484 482
485 483 Kernel.prototype._handle_status_message = function (msg) {
486 484 var execution_state = msg.content.execution_state;
487 485 var parent_id = msg.parent_header.msg_id;
488 486
489 487 // dispatch status msg callbacks, if any
490 488 var callbacks = this.get_callbacks_for_msg(parent_id);
491 489 if (callbacks && callbacks.iopub && callbacks.iopub.status) {
492 490 try {
493 491 callbacks.iopub.status(msg);
494 492 } catch (e) {
495 493 console.log("Exception in status msg handler", e, e.stack);
496 494 }
497 495 }
498 496
499 497 if (execution_state === 'busy') {
500 498 $([IPython.events]).trigger('status_busy.Kernel', {kernel: this});
501 499 } else if (execution_state === 'idle') {
502 500 // clear callbacks on idle, there can be no more
503 501 if (callbacks !== undefined) {
504 502 delete callbacks.iopub;
505 503 delete callbacks.input;
506 504 if (!callbacks.shell) {
507 505 this.clear_callbacks_for_msg(parent_id);
508 506 }
509 507 }
510 508 // trigger status_idle event
511 509 $([IPython.events]).trigger('status_idle.Kernel', {kernel: this});
512 510 } else if (execution_state === 'restarting') {
513 511 // autorestarting is distinct from restarting,
514 512 // in that it means the kernel died and the server is restarting it.
515 513 // status_restarting sets the notification widget,
516 514 // autorestart shows the more prominent dialog.
517 515 $([IPython.events]).trigger('status_autorestarting.Kernel', {kernel: this});
518 516 $([IPython.events]).trigger('status_restarting.Kernel', {kernel: this});
519 517 } else if (execution_state === 'dead') {
520 518 this.stop_channels();
521 519 $([IPython.events]).trigger('status_dead.Kernel', {kernel: this});
522 520 }
523 521 };
524 522
525 523
526 524 // handle clear_output message
527 525 Kernel.prototype._handle_clear_output = function (msg) {
528 526 var callbacks = this.get_callbacks_for_msg(msg.parent_header.msg_id);
529 527 if (!callbacks || !callbacks.iopub) {
530 528 return;
531 529 }
532 530 var callback = callbacks.iopub.clear_output;
533 531 if (callback) {
534 532 callback(msg);
535 533 }
536 534 };
537 535
538 536
539 537 // handle an output message (pyout, display_data, etc.)
540 538 Kernel.prototype._handle_output_message = function (msg) {
541 539 var callbacks = this.get_callbacks_for_msg(msg.parent_header.msg_id);
542 540 if (!callbacks || !callbacks.iopub) {
543 541 return;
544 542 }
545 543 var callback = callbacks.iopub.output;
546 544 if (callback) {
547 545 callback(msg);
548 546 }
549 547 };
550 548
551 549 // dispatch IOPub messages to respective handlers.
552 550 // each message type should have a handler.
553 551 Kernel.prototype._handle_iopub_message = function (e) {
554 552 var msg = $.parseJSON(e.data);
555 553
556 554 var handler = this.get_iopub_handler(msg.header.msg_type);
557 555 if (handler !== undefined) {
558 556 handler(msg);
559 557 }
560 558 };
561 559
562 560
563 561 Kernel.prototype._handle_input_request = function (e) {
564 562 var request = $.parseJSON(e.data);
565 563 var header = request.header;
566 564 var content = request.content;
567 565 var metadata = request.metadata;
568 566 var msg_type = header.msg_type;
569 567 if (msg_type !== 'input_request') {
570 568 console.log("Invalid input request!", request);
571 569 return;
572 570 }
573 571 var callbacks = this.get_callbacks_for_msg(request.parent_header.msg_id);
574 572 if (callbacks) {
575 573 if (callbacks.input) {
576 574 callbacks.input(request);
577 575 }
578 576 }
579 577 };
580 578
581 579
582 580 IPython.Kernel = Kernel;
583 581
584 582 return IPython;
585 583
586 584 }(IPython));
587 585
General Comments 0
You need to be logged in to leave comments. Login now