kernel.js
387 lines
| 13.2 KiB
| application/javascript
|
JavascriptLexer
Brian E. Granger
|
r4609 | //---------------------------------------------------------------------------- | ||
// 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. | ||||
//---------------------------------------------------------------------------- | ||||
Brian E. Granger
|
r4349 | |||
//============================================================================ | ||||
// Kernel | ||||
//============================================================================ | ||||
Brian E. Granger
|
r4352 | var IPython = (function (IPython) { | ||
Brian E. Granger
|
r4349 | |||
Brian E. Granger
|
r4352 | var utils = IPython.utils; | ||
Brian Granger
|
r7168 | // Initialization and connection. | ||
var Kernel = function (base_url) { | ||||
Brian E. Granger
|
r4352 | this.kernel_id = null; | ||
Brian E. Granger
|
r4545 | this.shell_channel = null; | ||
this.iopub_channel = null; | ||||
Brian Granger
|
r7168 | this.base_url = base_url; | ||
Brian E. Granger
|
r4545 | this.running = false; | ||
MinRK
|
r4694 | this.username = "username"; | ||
this.session_id = utils.uuid(); | ||||
Brian Granger
|
r7168 | this._msg_callbacks = {}; | ||
Brian E. Granger
|
r4612 | if (typeof(WebSocket) !== 'undefined') { | ||
Stefan van der Walt
|
r5479 | this.WebSocket = WebSocket; | ||
Brian E. Granger
|
r4612 | } else if (typeof(MozWebSocket) !== 'undefined') { | ||
Stefan van der Walt
|
r5479 | this.WebSocket = MozWebSocket; | ||
Brian E. Granger
|
r4611 | } else { | ||
MinRK
|
r5253 | 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.'); | ||
Brian E. Granger
|
r4611 | }; | ||
Brian E. Granger
|
r4352 | }; | ||
Brian Granger
|
r7168 | Kernel.prototype._get_msg = function (msg_type, content) { | ||
Brian E. Granger
|
r4352 | var msg = { | ||
header : { | ||||
msg_id : utils.uuid(), | ||||
MinRK
|
r4694 | username : this.username, | ||
session : this.session_id, | ||||
Brian E. Granger
|
r4352 | msg_type : msg_type | ||
}, | ||||
content : content, | ||||
parent_header : {} | ||||
}; | ||||
return msg; | ||||
Stefan van der Walt
|
r5479 | }; | ||
Brian E. Granger
|
r4352 | |||
Brian Granger
|
r7168 | Kernel.prototype.start = function (notebook_id) { | ||
Brian E. Granger
|
r4352 | var that = this; | ||
Brian E. Granger
|
r4545 | if (!this.running) { | ||
var qs = $.param({notebook:notebook_id}); | ||||
Stefan van der Walt
|
r5479 | var url = this.base_url + '?' + qs; | ||
Brian E. Granger
|
r5106 | $.post(url, | ||
Brian Granger
|
r7168 | $.proxy(that._kernel_started,that), | ||
Brian E. Granger
|
r4545 | 'json' | ||
); | ||||
}; | ||||
}; | ||||
Brian Granger
|
r7168 | Kernel.prototype.restart = function () { | ||
Brian Granger
|
r6047 | $([IPython.events]).trigger('status_restarting.Kernel'); | ||
Brian E. Granger
|
r4545 | var that = this; | ||
if (this.running) { | ||||
this.stop_channels(); | ||||
Brian Granger
|
r7168 | var url = this.kernel_url + "/restart"; | ||
Brian E. Granger
|
r4545 | $.post(url, | ||
Brian Granger
|
r7168 | $.proxy(that._kernel_started, that), | ||
Brian E. Granger
|
r4545 | 'json' | ||
); | ||||
}; | ||||
Brian E. Granger
|
r4352 | }; | ||
Brian Granger
|
r7168 | Kernel.prototype._kernel_started = function (json) { | ||
console.log("Kernel started: ", json.kernel_id); | ||||
Brian E. Granger
|
r4545 | this.running = true; | ||
Brian E. Granger
|
r4572 | this.kernel_id = json.kernel_id; | ||
this.ws_url = json.ws_url; | ||||
Brian E. Granger
|
r4352 | this.kernel_url = this.base_url + "/" + this.kernel_id; | ||
Brian E. Granger
|
r4545 | this.start_channels(); | ||
Brian Granger
|
r7168 | this.shell_channel.onmessage = $.proxy(this._handle_shell_reply,this); | ||
this.iopub_channel.onmessage = $.proxy(this._handle_iopub_reply,this); | ||||
Brian E. Granger
|
r4352 | }; | ||
Brian Granger
|
r7168 | |||
MinRK
|
r5255 | Kernel.prototype._websocket_closed = function(ws_url, early){ | ||
var msg; | ||||
var parent_item = $('body'); | ||||
if (early) { | ||||
Brian Granger
|
r6047 | msg = "Websocket connection to " + ws_url + " could not be established." + | ||
" You will NOT be able to run code." + | ||||
MinRK
|
r5255 | " 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" + | ||||
Stefan van der Walt
|
r5479 | " server's configuration."; | ||
MinRK
|
r5255 | } else { | ||
Brian Granger
|
r6047 | msg = "Websocket connection closed unexpectedly." + | ||
Stefan van der Walt
|
r5479 | " The kernel will no longer be responsive."; | ||
MinRK
|
r5255 | } | ||
var dialog = $('<div/>'); | ||||
dialog.html(msg); | ||||
parent_item.append(dialog); | ||||
dialog.dialog({ | ||||
resizable: false, | ||||
modal: true, | ||||
title: "Websocket closed", | ||||
Brian Granger
|
r6061 | closeText: "", | ||
close: function(event, ui) {$(this).dialog('destroy').remove();}, | ||||
MinRK
|
r5255 | buttons : { | ||
Brian Granger
|
r6061 | "OK": function () { | ||
MinRK
|
r5255 | $(this).dialog('close'); | ||
} | ||||
} | ||||
}); | ||||
Brian Granger
|
r7168 | |||
Stefan van der Walt
|
r5479 | }; | ||
Brian E. Granger
|
r4352 | |||
Brian E. Granger
|
r4545 | Kernel.prototype.start_channels = function () { | ||
MinRK
|
r5253 | var that = this; | ||
Brian E. Granger
|
r4545 | this.stop_channels(); | ||
Brian E. Granger
|
r4572 | var ws_url = this.ws_url + this.kernel_url; | ||
console.log("Starting WS:", ws_url); | ||||
Brian E. Granger
|
r4611 | this.shell_channel = new this.WebSocket(ws_url + "/shell"); | ||
this.iopub_channel = new this.WebSocket(ws_url + "/iopub"); | ||||
MinRK
|
r4707 | send_cookie = function(){ | ||
this.send(document.cookie); | ||||
Stefan van der Walt
|
r5479 | }; | ||
MinRK
|
r5253 | var already_called_onclose = false; // only alert once | ||
ws_closed_early = function(evt){ | ||||
if (already_called_onclose){ | ||||
return; | ||||
} | ||||
already_called_onclose = true; | ||||
if ( ! evt.wasClean ){ | ||||
MinRK
|
r5255 | that._websocket_closed(ws_url, true); | ||
MinRK
|
r5253 | } | ||
Stefan van der Walt
|
r5479 | }; | ||
MinRK
|
r5253 | ws_closed_late = function(evt){ | ||
if (already_called_onclose){ | ||||
return; | ||||
} | ||||
already_called_onclose = true; | ||||
if ( ! evt.wasClean ){ | ||||
MinRK
|
r5255 | that._websocket_closed(ws_url, false); | ||
MinRK
|
r5253 | } | ||
Stefan van der Walt
|
r5479 | }; | ||
MinRK
|
r4707 | this.shell_channel.onopen = send_cookie; | ||
MinRK
|
r5253 | this.shell_channel.onclose = ws_closed_early; | ||
MinRK
|
r4707 | this.iopub_channel.onopen = send_cookie; | ||
MinRK
|
r5253 | 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); | ||||
Brian E. Granger
|
r4545 | }; | ||
Brian E. Granger
|
r4352 | |||
Brian E. Granger
|
r4545 | Kernel.prototype.stop_channels = function () { | ||
if (this.shell_channel !== null) { | ||||
Stefan van der Walt
|
r5479 | this.shell_channel.onclose = function (evt) {}; | ||
Brian E. Granger
|
r4545 | this.shell_channel.close(); | ||
this.shell_channel = null; | ||||
}; | ||||
if (this.iopub_channel !== null) { | ||||
Stefan van der Walt
|
r5479 | this.iopub_channel.onclose = function (evt) {}; | ||
Brian E. Granger
|
r4545 | this.iopub_channel.close(); | ||
this.iopub_channel = null; | ||||
}; | ||||
}; | ||||
Brian Granger
|
r7168 | // Main public methods. | ||
Kernel.prototype.object_info_request = function (objname, callbacks) { | ||||
// When calling this method pass a callbacks structure of the form: | ||||
// | ||||
// callbacks = { | ||||
// 'object_info_reply': object_into_reply_callback | ||||
// } | ||||
// | ||||
// The object_info_reply_callback will be passed the content object of the | ||||
// object_into_reply message documented here: | ||||
// | ||||
// http://ipython.org/ipython-doc/dev/development/messaging.html#object-information | ||||
Matthias BUSSONNIER
|
r5522 | if(typeof(objname)!=null && objname!=null) | ||
Matthias BUSSONNIER
|
r5409 | { | ||
var content = { | ||||
oname : objname.toString(), | ||||
}; | ||||
Brian Granger
|
r7168 | var msg = this._get_msg("object_info_request", content); | ||
Matthias BUSSONNIER
|
r5409 | this.shell_channel.send(JSON.stringify(msg)); | ||
Brian Granger
|
r7168 | this.set_callbacks_for_msg(msg.header.msg_id, callbacks); | ||
Matthias BUSSONNIER
|
r5409 | return msg.header.msg_id; | ||
} | ||||
return; | ||||
Matthias BUSSONNIER
|
r5397 | } | ||
Brian Granger
|
r7176 | Kernel.prototype.execute = function (code, callbacks, options) { | ||
// 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 | ||||
// } | ||||
// | ||||
Brian Granger
|
r7168 | // 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, | ||||
Brian Granger
|
r7223 | // 'set_next_input': set_next_input_callback | ||
Brian Granger
|
r7168 | // } | ||
// | ||||
// The execute_reply_callback will be passed the content object 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 object 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. | ||||
// | ||||
Brian Granger
|
r7223 | // The set_next_input_callback will bepassed the text that should become the next | ||
// input cell. | ||||
Brian Granger
|
r7176 | |||
Brian E. Granger
|
r4352 | var content = { | ||
code : code, | ||||
Brian Granger
|
r7176 | silent : true, | ||
Brian E. Granger
|
r4352 | user_variables : [], | ||
MinRK
|
r4975 | user_expressions : {}, | ||
Stefan van der Walt
|
r5479 | allow_stdin : false | ||
Brian E. Granger
|
r4352 | }; | ||
Brian Granger
|
r7176 | $.extend(true, content, options) | ||
Brian Granger
|
r7168 | var msg = this._get_msg("execute_request", content); | ||
Brian E. Granger
|
r4352 | this.shell_channel.send(JSON.stringify(msg)); | ||
Brian Granger
|
r7168 | this.set_callbacks_for_msg(msg.header.msg_id, callbacks); | ||
Brian E. Granger
|
r4352 | return msg.header.msg_id; | ||
Stefan van der Walt
|
r5479 | }; | ||
Brian E. Granger
|
r4352 | |||
Brian Granger
|
r7168 | Kernel.prototype.complete = function (line, cursor_pos, callbacks) { | ||
// 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 | ||||
Brian Granger
|
r7212 | callbacks = callbacks || {}; | ||
Brian Granger
|
r4388 | var content = { | ||
text : '', | ||||
line : line, | ||||
cursor_pos : cursor_pos | ||||
}; | ||||
Brian Granger
|
r7168 | var msg = this._get_msg("complete_request", content); | ||
Brian Granger
|
r4388 | this.shell_channel.send(JSON.stringify(msg)); | ||
Brian Granger
|
r7168 | this.set_callbacks_for_msg(msg.header.msg_id, callbacks); | ||
Brian Granger
|
r4388 | return msg.header.msg_id; | ||
Stefan van der Walt
|
r5479 | }; | ||
Brian Granger
|
r4388 | |||
Brian E. Granger
|
r4352 | Kernel.prototype.interrupt = function () { | ||
Brian E. Granger
|
r4545 | if (this.running) { | ||
Brian Granger
|
r6047 | $([IPython.events]).trigger('status_interrupting.Kernel'); | ||
Brian E. Granger
|
r4545 | $.post(this.kernel_url + "/interrupt"); | ||
}; | ||||
Brian E. Granger
|
r4349 | }; | ||
Brian E. Granger
|
r4496 | Kernel.prototype.kill = function () { | ||
Brian E. Granger
|
r4545 | if (this.running) { | ||
this.running = false; | ||||
var settings = { | ||||
cache : false, | ||||
Stefan van der Walt
|
r5479 | type : "DELETE" | ||
Brian E. Granger
|
r4545 | }; | ||
$.ajax(this.kernel_url, settings); | ||||
Brian E. Granger
|
r4496 | }; | ||
}; | ||||
Brian Granger
|
r7168 | |||
// 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) { | ||||
Brian Granger
|
r7212 | this._msg_callbacks[msg_id] = callbacks || {}; | ||
Brian Granger
|
r7168 | } | ||
Kernel.prototype._handle_shell_reply = function (e) { | ||||
reply = $.parseJSON(e.data); | ||||
var header = reply.header; | ||||
var content = reply.content; | ||||
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); | ||||
} | ||||
}; | ||||
Brian Granger
|
r7223 | if (content.payload !== undefined) { | ||
Brian Granger
|
r7168 | var payload = content.payload || []; | ||
Brian Granger
|
r7223 | this._handle_payload(callbacks, payload); | ||
Brian Granger
|
r7168 | } | ||
}; | ||||
Brian Granger
|
r7223 | Kernel.prototype._handle_payload = function (callbacks, payload) { | ||
Brian Granger
|
r7168 | 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<l; i++) { | ||||
if (payload[i].source === 'IPython.zmq.page.page') { | ||||
var data = {'text':payload[i].text} | ||||
$([IPython.events]).trigger('open_with_text.Pager', data); | ||||
Brian Granger
|
r7223 | } else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') { | ||
if (callbacks.set_next_input !== undefined) { | ||||
callbacks.set_next_input(payload[i].text) | ||||
} | ||||
Brian Granger
|
r7168 | } | ||
}; | ||||
}; | ||||
Kernel.prototype._handle_iopub_reply = function (e) { | ||||
reply = $.parseJSON(e.data); | ||||
var content = reply.content; | ||||
var msg_type = reply.header.msg_type; | ||||
var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id); | ||||
if (msg_type !== 'status' && callbacks === undefined) { | ||||
// Message not from one of this notebook's cells and there are no | ||||
// callbacks to handle it. | ||||
return; | ||||
} | ||||
var output_types = ['stream','display_data','pyout','pyerr']; | ||||
if (output_types.indexOf(msg_type) >= 0) { | ||||
var cb = callbacks['output']; | ||||
if (cb !== undefined) { | ||||
cb(msg_type, content); | ||||
} | ||||
} else if (msg_type === 'status') { | ||||
if (content.execution_state === 'busy') { | ||||
$([IPython.events]).trigger('status_busy.Kernel'); | ||||
} else if (content.execution_state === 'idle') { | ||||
$([IPython.events]).trigger('status_idle.Kernel'); | ||||
} else if (content.execution_state === 'dead') { | ||||
this.stop_channels(); | ||||
$([IPython.events]).trigger('status_dead.Kernel'); | ||||
}; | ||||
} else if (msg_type === 'clear_output') { | ||||
var cb = callbacks['clear_output']; | ||||
if (cb !== undefined) { | ||||
cb(content); | ||||
} | ||||
}; | ||||
}; | ||||
Brian E. Granger
|
r4352 | IPython.Kernel = Kernel; | ||
return IPython; | ||||
}(IPython)); | ||||