Skip to content

Commit

Permalink
bin: Add flag --ijs-startup-script=path
Browse files Browse the repository at this point in the history
* This flag can be used to run a script at notebook startup.

* The flag also admits a folder, in which case all the scripts in the
  folder will be run at notebook startup. The scripts will be run in
  alphabetical order.
  • Loading branch information
n-riesco committed Jan 27, 2016
1 parent 8637a3e commit af04214
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 67 deletions.
14 changes: 12 additions & 2 deletions bin/ijavascript.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,12 @@ var usage = (
" --ijs-install=[local|global] install IJavascript kernel\n" +
" --ijs-install-kernel same as --ijs-install=local\n" +
" (for backwards-compatibility)\n" +
" --ijs-protocol=version set protocol version, e.g. 4.1\n" +
" --ijs-protocol=version set protocol version, e.g. 4.1\n" +
" --ijs-startup-script=path run script on startup\n" +
" (path can be a file or a folder)\n" +
" --ijs-working-dir=path set Javascript session working directory\n" +
" (default = current working directory)\n" +
" --version show IJavascript version\n" +
" --version show IJavascript version\n" +
"\n" +
"and any other options recognised by the IPython notebook; run:\n" +
"\n" +
Expand All @@ -86,6 +88,7 @@ var usage = (
* @property {Boolean} context.flag.debug --ijs-debug
* @property {String} context.flag.install --ijs-install=[local|global]
* @property {String} context.flag.cwd --ijs-working-dir=path
* @property {String} context.flag.startup --ijs-startup-script=path
* @property context.args
* @property {String[]} context.args.kernel Command arguments to run kernel
* @property {String[]} context.args.frontend Command arguments to run frontend
Expand Down Expand Up @@ -182,6 +185,9 @@ function parseCommandArgs(context) {
context.protocol.version.split(".", 1)[0]
);

} else if (e.lastIndexOf("--ijs-startup-script=", 0) === 0) {
context.flag.startup = fs.realpathSync(e.slice(21));

} else if (e.lastIndexOf("--ijs-working-dir=", 0) === 0) {
context.flag.cwd = fs.realpathSync(e.slice(18));

Expand All @@ -202,6 +208,10 @@ function parseCommandArgs(context) {
}
});

if (context.flag.startup) {
context.args.kernel.push("--startup-script=" + context.flag.startup);
}

if (context.flag.cwd) {
context.args.kernel.push("--session-working-dir=" + context.flag.cwd);
}
Expand Down
217 changes: 152 additions & 65 deletions lib/kernel.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ var DEBUG = false;

var console = require("console");
var fs = require("fs");
var path = require("path");
var util = require("util");

// Parse command arguments
Expand All @@ -50,29 +51,35 @@ var config = {
var usage = (
"Usage: node kernel.js " +
"[--debug] " +
"[--protocol=Major[.minor[.patch]]] " +
"[--session-working-dir=path " +
"[--hide-undefined] " +
"[--protocol=Major[.minor[.patch]]] " +
"[--session-working-dir=path] " +
"[--startup-script=path] " +
"connection_file"
);

var FLAG_DEBUG = "--debug";
var FLAG_HIDE_UNDEFINED = "--hide-undefined";
var FLAG_PROTOCOL = "--protocol=";
var FLAG_CWD = "--session-working-dir=";
var FLAG_HIDE_UNDEFINED = "--hide-undefined";
var FLAG_STARTUP_SCRIPT = "--startup-script=";
try {
process.argv.slice(2).forEach(function(arg) {
if (arg === FLAG_DEBUG) {
DEBUG = true;

} else if (arg === FLAG_HIDE_UNDEFINED) {
config.hideUndefined = true;

} else if (arg.slice(0, FLAG_PROTOCOL.length) === FLAG_PROTOCOL) {
config.protocolVersion = arg.slice(FLAG_PROTOCOL.length);

} else if (arg.slice(0, FLAG_CWD.length) === FLAG_CWD) {
config.nel.cwd = arg.slice(FLAG_CWD.length);

} else if (arg === FLAG_HIDE_UNDEFINED) {
config.hideUndefined = true;
} else if (
arg.slice(0, FLAG_STARTUP_SCRIPT.length) === FLAG_STARTUP_SCRIPT) {
config.startupScript = arg.slice(FLAG_STARTUP_SCRIPT.length);

} else if (!config.connection) {
config.connection = fs.readFileSync(arg);
Expand Down Expand Up @@ -112,6 +119,12 @@ var zmq = require("jmp").zmq; // ZMQ bindings
* @param {module:nel~Config} config.nel Javascript session configuration.
*
* @param {String} config.protocolVersion IPython/Jupyter protocol version.
*
* @param {String} config.startupScript Path to a Javascript file to be run
* on session startup. Path to a folder
* also accepted, in which case all the
* Javascript files in the folder will
* be run.
*/
function Kernel(config) {
/**
Expand Down Expand Up @@ -158,6 +171,14 @@ function Kernel(config) {
*/
this.session = new Session(config.nel);

/**
* Path to a Javascript file to be run on session startup. Path to a folder
* also accepted, in which case all the Javascript files in the folder will
* be run.
* @member {String}
*/
this.startupScript = config.startupScript;

/**
* Number of visible execution requests
* @member {Number}
Expand Down Expand Up @@ -188,11 +209,94 @@ function Kernel(config) {
require("./handlers_v4.js") :
require("./handlers_v5.js");

this._relayStandardStreams();

this._bindSockets();

this._initSession();
}

/**
* Bind kernel sockets and hook listeners
*
* @private
*/
Kernel.prototype._bindSockets = function() {
var address = "tcp://" + this.connectionConfig.ip + ":";

this.hbSocket.bind(address + this.connectionConfig.hb_port);
this.hbSocket.on("message", onHBMessage.bind(this));

this.iopubSocket.bind(address + this.connectionConfig.iopub_port);

this.shellSocket.bind(address + this.connectionConfig.shell_port);
this.shellSocket.on("message", onShellMessage.bind(this));

this.controlSocket.bind(address + this.connectionConfig.control_port);
this.controlSocket.on("message", onControlMessage.bind(this));

function onHBMessage(message) {
this.hbSocket.send(message);
}

function onShellMessage(msg) {
if (!msg.signatureOK) {
console.error(
"KERNEL: Bad message signature: %s", util.inspect(msg)
);
return;
}

var msg_type = msg.header.msg_type;
if (this.handlers.hasOwnProperty(msg_type)) {
msg.respond(this.iopubSocket, 'status', {
execution_state: 'busy'
});

try {
this.handlers[msg_type].call(this, msg);
} catch (e) {
console.error(
"KERNEL: Exception in %s handler: %s", msg_type, e.stack
);
}

msg.respond(this.iopubSocket, 'status', {
execution_state: 'idle'
});
} else {
// Ignore unimplemented msg_type requests
console.warn(
"KERNEL: SHELL_SOCKET: Unhandled message type:", msg_type
);
}
}

function onControlMessage(msg) {
if (!msg.signatureOK) {
console.error(
"KERNEL: Bad message signature: %s", util.inspect(msg)
);
return;
}

if (msg.header.msg_type === "shutdown_request") {
this.handlers.shutdown_request.call(this, msg);
} else {
// Ignore unimplemented msg_type requests
console.warn("KERNEL: CONTROL: Unhandled message type:", msg_type);
}
}
};

/**
* Initialise session
*
* @private
*/
Kernel.prototype._initSession = function() {
this._relayStandardStreams();
this._runStartupScripts();
};

/**
* Hook listeners to relay the standard streams onto the IOPub socket
*
Expand Down Expand Up @@ -264,76 +368,59 @@ Kernel.prototype._relayStandardStreams = function() {
};

/**
* Bind kernel sockets and hook listeners
* Run startup scripts
*
* @private
*/
Kernel.prototype._bindSockets = function() {
var address = "tcp://" + this.connectionConfig.ip + ":";

this.hbSocket.bind(address + this.connectionConfig.hb_port);
this.hbSocket.on("message", onHBMessage.bind(this));

this.iopubSocket.bind(address + this.connectionConfig.iopub_port);

this.shellSocket.bind(address + this.connectionConfig.shell_port);
this.shellSocket.on("message", onShellMessage.bind(this));

this.controlSocket.bind(address + this.connectionConfig.control_port);
this.controlSocket.on("message", onControlMessage.bind(this));

function onHBMessage(message) {
this.hbSocket.send(message);
}

function onShellMessage(msg) {
if (!msg.signatureOK) {
console.error(
"KERNEL: Bad message signature: %s", util.inspect(msg)
);
return;
}

var msg_type = msg.header.msg_type;
if (this.handlers.hasOwnProperty(msg_type)) {
msg.respond(this.iopubSocket, 'status', {
execution_state: 'busy'
Kernel.prototype._runStartupScripts = function() {
var startupScripts;

if (this.startupScript) {
var stats = fs.lstatSync(this.startupScript);
if (stats.isDirectory()) {
var dir = this.startupScript;
startupScripts = fs.readdirSync(dir).filter(function(filename) {
var ext = filename.slice(filename.length - 3).toLowerCase();
return ext === ".js";
}).sort().map(function(filename) {
return path.join(dir, filename);
});

try {
this.handlers[msg_type].call(this, msg);
} catch (e) {
console.error(
"KERNEL: Exception in %s handler: %s", msg_type, e.stack
);
}
} else if (stats.isFile()) {
startupScripts = [this.startupScript];

msg.respond(this.iopubSocket, 'status', {
execution_state: 'idle'
});
} else {
// Ignore unimplemented msg_type requests
console.warn(
"KERNEL: SHELL_SOCKET: Unhandled message type:", msg_type
);
startupScripts = [];
}
}

function onControlMessage(msg) {
if (!msg.signatureOK) {
console.error(
"KERNEL: Bad message signature: %s", util.inspect(msg)
);
if (DEBUG) console.log("KERNEL: STARTUP: " + startupScripts);

startupScripts.forEach((function(script) {
var code;

try {
code = fs.readFileSync(script).toString();
} catch (e) {
var msg = "KERNEL: STARTUP: Cannot read '" + script + "'";
if (DEBUG) console.log(msg);
return;
}

if (msg.header.msg_type === "shutdown_request") {
this.handlers.shutdown_request.call(this, msg);
} else {
// Ignore unimplemented msg_type requests
console.warn("KERNEL: CONTROL: Unhandled message type:", msg_type);
}
}
var task = {
action: "run",
code: code,
onSuccess: function() {
var msg = "KERNEL: STARTUP: '" + script + "' run successfuly";
if (DEBUG) console.log(msg);
},
onError: function() {
var msg = "KERNEL: STARTUP: '" + script + "' failed to run";
if (DEBUG) console.log(msg);
},
};
this.session.run(task);
}).bind(this));
};

/**
Expand Down Expand Up @@ -382,7 +469,7 @@ Kernel.prototype.restart = function(restartCB) {
if (DEBUG) console.log("KERNEL: Being restarted");

this.session.restart("SIGTERM", (function() {
this._relayStandardStreams();
this._initSession();
if (restartCB) {
restartCB();
}
Expand Down

0 comments on commit af04214

Please sign in to comment.