//---------------------------------------------------------------------------- // Copyright (C) 2008-2011 The IPython Development Team // // Distributed under the terms of the BSD License. The full license is in // the file COPYING, distributed as part of this software. //---------------------------------------------------------------------------- //============================================================================ // Kernel //============================================================================ /** * @module IPython * @namespace IPython * @submodule Kernel */ var IPython = (function (IPython) { var utils = IPython.utils; // Initialization and connection. /** * A Kernel Class to communicate with the Python kernel * @Class Kernel */ var Kernel = function (base_url) { this.kernel_id = null; this.shell_channel = null; this.iopub_channel = null; this.base_url = base_url; this.running = false; this.username = "username"; this.session_id = utils.uuid(); this._msg_callbacks = {}; if (typeof(WebSocket) !== 'undefined') { this.WebSocket = WebSocket; } else if (typeof(MozWebSocket) !== 'undefined') { this.WebSocket = MozWebSocket; } else { 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.'); }; }; Kernel.prototype._get_msg = function (msg_type, content) { var msg = { header : { msg_id : utils.uuid(), username : this.username, session : this.session_id, msg_type : msg_type }, metadata : {}, content : content, parent_header : {} }; return msg; }; /** * Start the Python kernel * @method start */ Kernel.prototype.start = function (notebook_id) { var that = this; if (!this.running) { var qs = $.param({notebook:notebook_id}); var url = this.base_url + '?' + qs; $.post(url, $.proxy(that._kernel_started,that), 'json' ); }; }; /** * Restart the python kernel. * * Emit a 'status_restarting.Kernel' event with * the current object as parameter * * @method restart */ Kernel.prototype.restart = function () { $([IPython.events]).trigger('status_restarting.Kernel', {kernel: this}); var that = this; if (this.running) { this.stop_channels(); var url = this.kernel_url + "/restart"; $.post(url, $.proxy(that._kernel_started, that), 'json' ); }; }; Kernel.prototype._kernel_started = function (json) { console.log("Kernel started: ", json.kernel_id); this.running = true; this.kernel_id = json.kernel_id; this.ws_url = json.ws_url; this.kernel_url = this.base_url + "/" + this.kernel_id; this.start_channels(); this.shell_channel.onmessage = $.proxy(this._handle_shell_reply,this); this.iopub_channel.onmessage = $.proxy(this._handle_iopub_reply,this); $([IPython.events]).trigger('status_started.Kernel', {kernel: this}); }; Kernel.prototype._websocket_closed = function(ws_url, early){ var msg; var parent_item = $('body'); if (early) { msg = "Websocket connection to " + ws_url + " could not be established." + " You will NOT be able to run code." + " Your browser may not be compatible with the websocket version in the server," + " or if the url does not look right, there could be an error in the" + " server's configuration."; } else { IPython.notification_area.widget('kernel').set_message('Reconnecting Websockets', 1000); this.start_channels(); return; } var dialog = $('
'); dialog.html(msg); parent_item.append(dialog); dialog.dialog({ resizable: false, modal: true, title: "Websocket closed", closeText: "", close: function(event, ui) {$(this).dialog('destroy').remove();}, buttons : { "OK": function () { $(this).dialog('close'); } } }); }; /** * Start the `shell`and `iopub` channels. * Will stop and restart them if they already exist. * * @method start_channels */ Kernel.prototype.start_channels = function () { var that = this; this.stop_channels(); var ws_url = this.ws_url + this.kernel_url; console.log("Starting WS:", ws_url); this.shell_channel = new this.WebSocket(ws_url + "/shell"); this.iopub_channel = new this.WebSocket(ws_url + "/iopub"); send_cookie = function(){ this.send(document.cookie); }; var already_called_onclose = false; // only alert once var ws_closed_early = function(evt){ if (already_called_onclose){ return; } already_called_onclose = true; if ( ! evt.wasClean ){ that._websocket_closed(ws_url, true); } }; var ws_closed_late = function(evt){ if (already_called_onclose){ return; } already_called_onclose = true; if ( ! evt.wasClean ){ that._websocket_closed(ws_url, false); } }; this.shell_channel.onopen = send_cookie; this.shell_channel.onclose = ws_closed_early; this.iopub_channel.onopen = send_cookie; this.iopub_channel.onclose = ws_closed_early; // switch from early-close to late-close message after 1s setTimeout(function(){ that.shell_channel.onclose = ws_closed_late; that.iopub_channel.onclose = ws_closed_late; }, 1000); }; /** * Start the `shell`and `iopub` channels. * @method stop_channels */ Kernel.prototype.stop_channels = function () { if (this.shell_channel !== null) { this.shell_channel.onclose = function (evt) {}; this.shell_channel.close(); this.shell_channel = null; }; if (this.iopub_channel !== null) { this.iopub_channel.onclose = function (evt) {}; this.iopub_channel.close(); this.iopub_channel = null; }; }; // Main public methods. /** * Get info on object asynchronoulsy * * @async * @param objname {string} * @param callback {dict} * @method object_info_request * * @example * * When calling this method pass a callbacks structure of the form: * * callbacks = { * 'object_info_reply': object_info_reply_callback * } * * The `object_info_reply_callback` will be passed the content object of the * * `object_into_reply` message documented in * [IPython dev documentation](http://ipython.org/ipython-doc/dev/development/messaging.html#object-information) */ Kernel.prototype.object_info_request = function (objname, callbacks) { if(typeof(objname)!=null && objname!=null) { var content = { oname : objname.toString(), }; var msg = this._get_msg("object_info_request", content); this.shell_channel.send(JSON.stringify(msg)); this.set_callbacks_for_msg(msg.header.msg_id, callbacks); return msg.header.msg_id; } return; } /** * Execute given code into kernel, and pass result to callback. * * @async * @method execute * @param {string} code * @param callback {Object} With the following keys * @param callback.'execute_reply' {function} * @param callback.'output' {function} * @param callback.'clear_output' {function} * @param callback.'set_next_input' {function} * @param {object} [options] * @param [options.silent=false] {Boolean} * @param [options.user_expressions=empty_dict] {Dict} * @param [options.user_variables=empty_list] {List od Strings} * @param [options.allow_stdin=false] {Boolean} true|false * * @example * * The options object should contain the options for the execute call. Its default * values are: * * options = { * silent : true, * user_variables : [], * user_expressions : {}, * allow_stdin : false * } * * When calling this method pass a callbacks structure of the form: * * callbacks = { * 'execute_reply': execute_reply_callback, * 'output': output_callback, * 'clear_output': clear_output_callback, * 'set_next_input': set_next_input_callback * } * * The `execute_reply_callback` will be passed the content and metadata * objects of the `execute_reply` message documented * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#execute) * * The `output_callback` will be passed `msg_type` ('stream','display_data','pyout','pyerr') * of the output and the content and metadata objects of the PUB/SUB channel that contains the * output: * * http://ipython.org/ipython-doc/dev/development/messaging.html#messages-on-the-pub-sub-socket * * The `clear_output_callback` will be passed a content object that contains * stdout, stderr and other fields that are booleans, as well as the metadata object. * * The `set_next_input_callback` will be passed the text that should become the next * input cell. */ Kernel.prototype.execute = function (code, callbacks, options) { var content = { code : code, silent : true, user_variables : [], user_expressions : {}, allow_stdin : false }; $.extend(true, content, options) $([IPython.events]).trigger('execution_request.Kernel', {kernel: this, content:content}); var msg = this._get_msg("execute_request", content); this.shell_channel.send(JSON.stringify(msg)); this.set_callbacks_for_msg(msg.header.msg_id, callbacks); return msg.header.msg_id; }; /** * When calling this method pass a callbacks structure of the form: * * callbacks = { * 'complete_reply': complete_reply_callback * } * * The `complete_reply_callback` will be passed the content object of the * `complete_reply` message documented * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#complete) * * @method complete * @param line {integer} * @param cursor_pos {integer} * @param {dict} callbacks * @param callbacks.complete_reply {function} `complete_reply_callback` * */ Kernel.prototype.complete = function (line, cursor_pos, callbacks) { callbacks = callbacks || {}; var content = { text : '', line : line, cursor_pos : cursor_pos }; var msg = this._get_msg("complete_request", content); this.shell_channel.send(JSON.stringify(msg)); this.set_callbacks_for_msg(msg.header.msg_id, callbacks); return msg.header.msg_id; }; Kernel.prototype.interrupt = function () { if (this.running) { $([IPython.events]).trigger('status_interrupting.Kernel', {kernel: this}); $.post(this.kernel_url + "/interrupt"); }; }; Kernel.prototype.kill = function () { if (this.running) { this.running = false; var settings = { cache : false, type : "DELETE" }; $.ajax(this.kernel_url, settings); }; }; // Reply handlers. Kernel.prototype.get_callbacks_for_msg = function (msg_id) { var callbacks = this._msg_callbacks[msg_id]; return callbacks; }; Kernel.prototype.set_callbacks_for_msg = function (msg_id, callbacks) { this._msg_callbacks[msg_id] = callbacks || {}; } Kernel.prototype._handle_shell_reply = function (e) { var reply = $.parseJSON(e.data); $([IPython.events]).trigger('shell_reply.Kernel', {kernel: this, reply:reply}); var header = reply.header; var content = reply.content; var metadata = reply.metadata; var msg_type = header.msg_type; var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id); if (callbacks !== undefined) { var cb = callbacks[msg_type]; if (cb !== undefined) { cb(content, metadata); } }; if (content.payload !== undefined) { var payload = content.payload || []; this._handle_payload(callbacks, payload); } }; Kernel.prototype._handle_payload = function (callbacks, payload) { var l = payload.length; // Payloads are handled by triggering events because we don't want the Kernel // to depend on the Notebook or Pager classes. for (var i=0; i