change python config, add jupyter and ein
This commit is contained in:
28
lisp/jupyter/js/Makefile
Normal file
28
lisp/jupyter/js/Makefile
Normal file
@@ -0,0 +1,28 @@
|
||||
SHELL = bash
|
||||
|
||||
NPM ?= $(shell command -v npm)
|
||||
ifeq ($(NPM),)
|
||||
$(error "Node not installed (https://nodejs.org/en/)")
|
||||
endif
|
||||
|
||||
YARN ?= $(shell command -v yarn)
|
||||
ifeq ($(YARN),)
|
||||
# If yarn isn't already installed, it is built locally
|
||||
YARN = ./node_modules/.bin/yarn
|
||||
endif
|
||||
|
||||
.PHONY: all build clean
|
||||
|
||||
all: build
|
||||
|
||||
clean:
|
||||
@rm -rf built/ 2>/dev/null || true
|
||||
|
||||
really-clean: clean
|
||||
@rm -rf node_modules 2>/dev/null || true
|
||||
|
||||
build: built/index.built.js
|
||||
|
||||
built/index.built.js:
|
||||
$(NPM) install
|
||||
$(YARN) run build --progress
|
||||
342
lisp/jupyter/js/emacs-jupyter.js
Normal file
342
lisp/jupyter/js/emacs-jupyter.js
Normal file
@@ -0,0 +1,342 @@
|
||||
// NOTE: Info on widgets http://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Low%20Level.html
|
||||
var disposable = require('@phosphor/disposable');
|
||||
var coreutils = require('@jupyterlab/coreutils');
|
||||
// The KernelFutureHandler allows comms to register their callbacks to be
|
||||
// called when messages are received in response to a request sent to the
|
||||
// kernel.
|
||||
var KernelFutureHandler = require('@jupyterlab/services/kernel/future').KernelFutureHandler;
|
||||
// The CommHandler object handles comm interaction to/from the kernel. It takes
|
||||
// a target_name, usually jupyter.widget, and a comm_id. It takes care of
|
||||
// sending comm messages to the kernel and calls the callback methods of a Comm
|
||||
// when a comm_msg is received from the kernel.
|
||||
//
|
||||
// A Comm object is essentially a wrapper around a CommHandler that updates the
|
||||
// CommHandler callbacks and registers callbacks on the futures created when a
|
||||
// Comm sends a message on the shell channel.
|
||||
var CommHandler = require('@jupyterlab/services/kernel/comm').CommHandler;
|
||||
|
||||
|
||||
// A CommManager takes care of registering new comm targets and creating new
|
||||
// comms and holding a list of all the live comms.
|
||||
|
||||
// It looks like I just ned to implement the IKernel interface and pass the
|
||||
// object that implements it to CommManager, this way I can create new comms
|
||||
// with CommManager.new_comm when handling comm_open messages. In the IKernel
|
||||
// interface, I'll just redirect all the message sending functions to Emacs.
|
||||
|
||||
// It looks like widgets send messages through the callbacks of a
|
||||
// KernelFutureHandler so I will have to redirect all received messages that
|
||||
// originated from a request generated by skewer.postJSON back to the
|
||||
// JavaScript environment. Emacs then acts as an intermediary, capturing kernel
|
||||
// messages and re-packaging them to send to the Javascript environment.
|
||||
//
|
||||
// It looks like whenever the kernel receives a message it accesse the correct
|
||||
// future object using this.futures.get and calls handleMsg function of the
|
||||
// future.
|
||||
//
|
||||
// The flow of message with respect to Comm objects is that Comm object send
|
||||
// shell messages, then widgets register callbacks on the future.
|
||||
|
||||
var EmacsJupyter = function(options, port) {
|
||||
var _this = this;
|
||||
|
||||
this.username = options.username || '';
|
||||
// This is the Jupyter session id
|
||||
this.clientId = options.clientId;
|
||||
this.isDisposed = false;
|
||||
// A mapping from comm_id's to promises that resolve to their open Comm
|
||||
// objects.
|
||||
this.commPromises = new Map();
|
||||
// The targetRegistry is a dictionary mapping target names to target
|
||||
// functions that are called whenever a new Comm is requested to be open by
|
||||
// the kernel. The target function gets called with the initial comm_open
|
||||
// message data and a comm handler for the new Comm.
|
||||
this.targetRegistry = {};
|
||||
// A mapping of msg_id's for messages sent to the kernel and their
|
||||
// KernelFutureHandler objects.
|
||||
this.futures = new Map();
|
||||
// The WidgetManager that connects comms to their corresponding widget
|
||||
// models, construct widget views, load widget modules, and get the current
|
||||
// widget state.
|
||||
this.widgetManager = null;
|
||||
this.widgetState = null;
|
||||
// The CommManager that registers the target names and their target
|
||||
// functions handles opening and closing comms for a particular
|
||||
// target name.
|
||||
this.commManager = null;
|
||||
this.messagePromise = new Promise(function (resolve) { resolve(); });
|
||||
|
||||
window.addEventListener("unload", function(event) {
|
||||
// TODO: Send widget state
|
||||
});
|
||||
|
||||
// Localhost
|
||||
this.wsPort = port;
|
||||
this.ws = new WebSocket("ws://127.0.0.1:" + port);
|
||||
this.ws.onopen = function () {
|
||||
// Ensure that Emacs knows which websocket connection corresponds to
|
||||
// each kernel client
|
||||
_this.ws.send(JSON.stringify({
|
||||
header: {
|
||||
msg_type: "connect",
|
||||
session: _this.clientId
|
||||
}
|
||||
}));
|
||||
};
|
||||
this.ws.onmessage = function(event) {
|
||||
if(_this.isDisposed) {
|
||||
return;
|
||||
}
|
||||
var msg = JSON.parse(event.data);
|
||||
_this.messagePromise =
|
||||
_this.messagePromise.then(function () {
|
||||
if(msg.buffers && msg.buffers.length > 0) {
|
||||
for(var i = 0; i < msg.buffers.length; i++) {
|
||||
var bin = atob(msg.buffers[i]);
|
||||
var len = bin.length;
|
||||
var buf = new Uint8Array(len);
|
||||
for(var j = 0; j < len; j++) {
|
||||
buf[j] = bin.charCodeAt(j);
|
||||
}
|
||||
msg.buffers[i] = buf.buffer;
|
||||
}
|
||||
}
|
||||
_this.handleMessage(msg);
|
||||
});
|
||||
};
|
||||
};
|
||||
exports.EmacsJupyter = EmacsJupyter;
|
||||
|
||||
EmacsJupyter.prototype.dispose = function () {
|
||||
if (this.isDisposed) {
|
||||
return;
|
||||
}
|
||||
this.isDisposed = true;
|
||||
this.commPromises.forEach(function (promise, key) {
|
||||
promise.then(function (comm) {
|
||||
comm.dispose();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
EmacsJupyter.prototype.registerCommTarget = function(targetName, callback) {
|
||||
var _this = this;
|
||||
this.targetRegistry[targetName] = callback;
|
||||
return new disposable.DisposableDelegate(function () {
|
||||
if (!_this.isDisposed) {
|
||||
delete _this.targetRegistry[targetName];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
EmacsJupyter.prototype.connectToComm = function (targetName, commId) {
|
||||
var _this = this;
|
||||
var id = commId || coreutils.uuid();
|
||||
if (this.commPromises.has(id)) {
|
||||
return this.commPromises.get(id);
|
||||
}
|
||||
var promise = Promise.resolve(void 0).then(function () {
|
||||
return new CommHandler(targetName, id, _this, function () {
|
||||
_this._unregisterComm(id);
|
||||
});
|
||||
});
|
||||
this.commPromises.set(id, promise);
|
||||
return promise;
|
||||
};
|
||||
|
||||
EmacsJupyter.prototype.handleCommOpen = function (msg) {
|
||||
var _this = this;
|
||||
var content = msg.content;
|
||||
if (this.isDisposed) {
|
||||
return;
|
||||
}
|
||||
var promise = this.loadObject(content.target_name,
|
||||
content.target_module,
|
||||
this.targetRegistry)
|
||||
.then(function (target) {
|
||||
var comm = new CommHandler(content.target_name,
|
||||
content.comm_id,
|
||||
_this, function () {
|
||||
_this._unregisterComm(content.comm_id);
|
||||
});
|
||||
var response;
|
||||
try {
|
||||
response = target(comm, msg);
|
||||
}
|
||||
catch (e) {
|
||||
comm.close();
|
||||
console.error('Exception opening new comm');
|
||||
throw (e);
|
||||
}
|
||||
return Promise.resolve(response).then(function () {
|
||||
if (_this.isDisposed) {
|
||||
return null;
|
||||
}
|
||||
return comm;
|
||||
});
|
||||
});
|
||||
this.commPromises.set(content.comm_id, promise);
|
||||
};
|
||||
|
||||
EmacsJupyter.prototype.handleCommClose = function (msg) {
|
||||
var _this = this;
|
||||
var content = msg.content;
|
||||
var promise = this.commPromises.get(content.comm_id);
|
||||
if (!promise) {
|
||||
console.error('Comm not found for comm id ' + content.comm_id);
|
||||
return;
|
||||
}
|
||||
promise.then(function (comm) {
|
||||
if (!comm) {
|
||||
return;
|
||||
}
|
||||
_this._unregisterComm(comm.commId);
|
||||
try {
|
||||
var onClose = comm.onClose;
|
||||
if (onClose) {
|
||||
onClose(msg);
|
||||
}
|
||||
comm.dispose();
|
||||
}
|
||||
catch (e) {
|
||||
console.error('Exception closing comm: ', e, e.stack, msg);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
EmacsJupyter.prototype.handleCommMsg = function (msg) {
|
||||
var promise = this.commPromises.get(msg.content.comm_id);
|
||||
if (!promise) {
|
||||
// We do have a registered comm for this comm id, ignore.
|
||||
return;
|
||||
}
|
||||
promise.then(function (comm) {
|
||||
if (!comm) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var onMsg = comm.onMsg;
|
||||
if (onMsg) {
|
||||
onMsg(msg);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.error('Exception handling comm msg: ', e, e.stack, msg);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
EmacsJupyter.prototype.loadObject = function(name, moduleName, registry) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
// Try loading the view module using require.js
|
||||
if (moduleName) {
|
||||
if (typeof window.require === 'undefined') {
|
||||
throw new Error('requirejs not found');
|
||||
}
|
||||
window.require([moduleName], function (mod) {
|
||||
if (mod[name] === void 0) {
|
||||
var msg = "Object '" + name + "' not found in module '" + moduleName + "'";
|
||||
reject(new Error(msg));
|
||||
}
|
||||
else {
|
||||
resolve(mod[name]);
|
||||
}
|
||||
}, reject);
|
||||
}
|
||||
else {
|
||||
if (registry && registry[name]) {
|
||||
resolve(registry[name]);
|
||||
}
|
||||
else {
|
||||
reject(new Error("Object '" + name + "' not found in registry"));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
EmacsJupyter.prototype._unregisterComm = function (commId) {
|
||||
this.commPromises.delete(commId);
|
||||
};
|
||||
|
||||
EmacsJupyter.prototype.sendShellMessage = function(msg, expectReply, disposeOnDone) {
|
||||
var _this = this;
|
||||
if (expectReply === void 0) { expectReply = false; }
|
||||
if (disposeOnDone === void 0) { disposeOnDone = true; }
|
||||
|
||||
var future = new KernelFutureHandler(function () {
|
||||
var msgId = msg.header.msg_id;
|
||||
_this.futures.delete(msgId);
|
||||
}, msg, expectReply, disposeOnDone, this);
|
||||
|
||||
this.ws.send(JSON.stringify(msg));
|
||||
this.futures.set(msg.header.msg_id, future);
|
||||
return future;
|
||||
};
|
||||
|
||||
EmacsJupyter.prototype.requestCommInfo = function(targetName) {
|
||||
var msg = {
|
||||
channel: 'shell',
|
||||
msg_type: 'comm_info_request',
|
||||
// A message ID will be added by Emacs anyway
|
||||
header: {msg_id: ''},
|
||||
content: {target_name: targetName}
|
||||
};
|
||||
var future = this.sendShellMessage(msg, true);
|
||||
return new Promise(function (resolve) {
|
||||
future.onReply = resolve;
|
||||
});
|
||||
};
|
||||
|
||||
EmacsJupyter.prototype.handleMessage = function(msg) {
|
||||
var _this = this;
|
||||
var parentHeader = msg.parent_header;
|
||||
var future = parentHeader && this.futures && this.futures.get(parentHeader.msg_id);
|
||||
if (future) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
try {
|
||||
future.handleMsg(msg);
|
||||
resolve(msg);
|
||||
} catch(err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return new Promise(function (resolve, reject) {
|
||||
switch(msg.msg_type) {
|
||||
// Special messages not really a Jupyter message
|
||||
case 'display_model':
|
||||
_this.widgetManager.get_model(msg.content.model_id).then(function (model) {
|
||||
_this.widgetManager.display_model(undefined, model);
|
||||
});
|
||||
break;
|
||||
case 'clear_display':
|
||||
var widget = _this.widgetManager.area;
|
||||
while(widget.firstChild) {
|
||||
widget.removeChild(widget.firstChild);
|
||||
}
|
||||
break;
|
||||
// Regular Jupyter messages
|
||||
case 'comm_open':
|
||||
_this.handleCommOpen(msg);
|
||||
// Periodically get the state of the widgetManager, this gets
|
||||
// sent to the browser when its unloaded.
|
||||
// _this.widgetManager.get_state({}).then(function (state) {
|
||||
// _this.widgetState = state;
|
||||
// });
|
||||
break;
|
||||
case 'comm_close':
|
||||
_this.handleCommClose(msg);
|
||||
break;
|
||||
case 'comm_msg':
|
||||
_this.handleCommMsg(msg);
|
||||
break;
|
||||
case 'status':
|
||||
// Comes from the comm info messages
|
||||
break;
|
||||
default:
|
||||
reject(new Error('Unhandled message', msg));
|
||||
};
|
||||
resolve(msg);
|
||||
});
|
||||
}
|
||||
}
|
||||
12
lisp/jupyter/js/index.js
Normal file
12
lisp/jupyter/js/index.js
Normal file
@@ -0,0 +1,12 @@
|
||||
window.CommManager = require('@jupyter-widgets/base').shims.services.CommManager;
|
||||
window.WidgetManager = require('./manager').WidgetManager;
|
||||
window.EmacsJupyter = require('./emacs-jupyter').EmacsJupyter;
|
||||
require('font-awesome/css/font-awesome.min.css');
|
||||
require('@jupyter-widgets/controls/css/widgets.built.css');
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
|
||||
var widget = document.createElement("div");
|
||||
widget.setAttribute("id", "widget");
|
||||
document.body.appendChild(widget);
|
||||
});
|
||||
82
lisp/jupyter/js/manager.js
Normal file
82
lisp/jupyter/js/manager.js
Normal file
@@ -0,0 +1,82 @@
|
||||
var base = require('@jupyter-widgets/base');
|
||||
var output = require('@jupyter-widgets/output');
|
||||
var controls = require('@jupyter-widgets/controls');
|
||||
var PhosphorWidget = require('@phosphor/widgets').Widget;
|
||||
|
||||
var defineWidgetModules = function () {
|
||||
if(window.define) {
|
||||
window.define('@jupyter-widgets/output', [], function () { return output; });
|
||||
window.define('@jupyter-widgets/base', [], function () { return base; });
|
||||
window.define('@jupyter-widgets/controls', [], function () { return controls; });
|
||||
} else {
|
||||
setTimeout(defineWidgetModules, 50);
|
||||
}
|
||||
};
|
||||
|
||||
// requirejs loading is async so it may not be available on this event
|
||||
window.addEventListener("DOMContentLoaded", function () {
|
||||
defineWidgetModules();
|
||||
});
|
||||
|
||||
var WidgetManager = exports.WidgetManager = function(kernel, area) {
|
||||
base.ManagerBase.call(this);
|
||||
this.kernel = kernel;
|
||||
this.area = area;
|
||||
};
|
||||
WidgetManager.prototype = Object.create(base.ManagerBase.prototype);
|
||||
|
||||
WidgetManager.prototype.loadClass = function(className, moduleName, moduleVersion) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
if (moduleName === '@jupyter-widgets/controls') {
|
||||
resolve(controls);
|
||||
} else if (moduleName === '@jupyter-widgets/base') {
|
||||
resolve(base);
|
||||
} else if (moduleName === '@jupyter-widgets/output')
|
||||
resolve(output);
|
||||
else {
|
||||
var fallback = function(err) {
|
||||
var failedId = err.requireModules && err.requireModules[0];
|
||||
if (failedId) {
|
||||
console.log('Falling back to unpkg.com for ' + moduleName + '@' + moduleVersion);
|
||||
window.require(['https://unpkg.com/' + moduleName + '@' + moduleVersion + '/dist/index.js'], resolve, reject);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
window.require([moduleName + '.js'], resolve, fallback);
|
||||
}
|
||||
}).then(function(module) {
|
||||
if (module[className]) {
|
||||
return module[className];
|
||||
} else {
|
||||
return Promise.reject('Class ' + className + ' not found in module ' + moduleName + '@' + moduleVersion);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
WidgetManager.prototype.display_view = function(msg, view, options) {
|
||||
var _this = this;
|
||||
return Promise.resolve(view).then(function(view) {
|
||||
PhosphorWidget.attach(view.pWidget, _this.area);
|
||||
view.on('remove', function() {
|
||||
console.log('View removed', view);
|
||||
});
|
||||
view.trigger('displayed');
|
||||
return view;
|
||||
});
|
||||
};
|
||||
|
||||
WidgetManager.prototype._get_comm_info = function() {
|
||||
return this.kernel.requestCommInfo(this.comm_target_name).then(function(reply) {
|
||||
return reply.content.comms;
|
||||
});
|
||||
};
|
||||
|
||||
WidgetManager.prototype._create_comm = function(targetName, commId, data, metadata) {
|
||||
// Construct a comm that already exists
|
||||
var comm = this.kernel.connectToComm(targetName, commId);
|
||||
if(data || metadata) {
|
||||
comm.open(data, metadata);
|
||||
}
|
||||
return Promise.resolve(new base.shims.services.Comm(comm));
|
||||
}
|
||||
33
lisp/jupyter/js/package.json
Normal file
33
lisp/jupyter/js/package.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "emacs-jupyter",
|
||||
"version": "0.3.0",
|
||||
"description": "Integrate emacs-jupyter with widgets in a browser.",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"clean": "rm -rf built",
|
||||
"build": "webpack",
|
||||
"test": "npm run test:default",
|
||||
"test:default": "echo \"No test specified\""
|
||||
},
|
||||
"author": "Nathaniel Nicandro",
|
||||
"license": "GPL-2.0-or-later",
|
||||
"dependencies": {
|
||||
"@jupyter-widgets/base": "^1.2.2",
|
||||
"@jupyter-widgets/controls": "^1.2.1",
|
||||
"@jupyter-widgets/output": "^1.0.10",
|
||||
"codemirror": "^5.9.0",
|
||||
"font-awesome": "^4.7.0",
|
||||
"npm": "^6.4.1",
|
||||
"yarn": "^1.12.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"css-loader": "^0.28.4",
|
||||
"file-loader": "^0.11.2",
|
||||
"json-loader": "^0.5.4",
|
||||
"raw-loader": "^0.5.1",
|
||||
"style-loader": "^0.18.1",
|
||||
"url-loader": "^1.1.2",
|
||||
"webpack": "^3.5.5"
|
||||
}
|
||||
}
|
||||
29
lisp/jupyter/js/webpack.config.js
Normal file
29
lisp/jupyter/js/webpack.config.js
Normal file
@@ -0,0 +1,29 @@
|
||||
var path = require('path');
|
||||
|
||||
module.exports = {
|
||||
entry: "./index.js",
|
||||
output: {
|
||||
filename: 'index.built.js',
|
||||
path: path.resolve(__dirname, 'built'),
|
||||
publicPath: 'built/'
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@jupyterlab/services/kernel/future': path.resolve(__dirname, 'node_modules/@jupyterlab/services/lib/kernel/future'),
|
||||
'@jupyterlab/services/kernel/comm': path.resolve(__dirname, 'node_modules/@jupyterlab/services/lib/kernel/comm')
|
||||
}
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{ test: /\.css$/, loader: "style-loader!css-loader" },
|
||||
// jquery-ui loads some images
|
||||
{ test: /\.(jpg|png|gif)$/, use: 'file-loader' },
|
||||
// required to load font-awesome
|
||||
{ test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, use: 'url-loader?mimetype=application/font-woff' },
|
||||
{ test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, use: 'url-loader?mimetype=application/font-woff' },
|
||||
{ test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, use: 'url-loader?mimetype=application/octet-stream' },
|
||||
{ test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, use: 'file-loader' },
|
||||
{ test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, use: 'url-loader?mimetype=image/svg+xml' }
|
||||
]
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user