@nteract/fs-kernels
Version:
A manager for the filesystem aspects of Juyter kernels
191 lines (190 loc) • 7.08 kB
JavaScript
;
/**
* This module contains methods that allow you to launch ("spawn") Jupyter
* kernels. You can spawn kernels either by name, by a `kernelSpec` or
* by a `kernelSpec` and its connection information.
*
* Usage example:
* ```js
* // Spawn a kernel by name
* var spawnResults = require('spawnteract').launch('python3');
*
* // Print the ip address and port for the shell channel
* console.log(spawnResults.config.ip + ':' + spawnResults.config.shell_port);
* ```
*
* You'll need to close `spawnResults.spawn` yourself as well as delete
* `spawnResults.connectionFile` from disk when finished.
*
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
/* eslint camelcase: 0 */
// ^--- #justjupyterthings
// tslint:disable:object-literal-sort-keys
// ^--- let keys for ports be in port id order
/**
*
*/
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const util_1 = __importDefault(require("util"));
const portfinder_1 = require("portfinder");
const uuid_1 = __importDefault(require("uuid"));
const execa_1 = __importDefault(require("execa"));
const mkdirp_1 = __importDefault(require("mkdirp"));
const jupyter_paths_1 = __importDefault(require("./jupyter-paths"));
const kernelspecs_1 = require("./kernelspecs");
const enchannel_zmq_backend_1 = require("enchannel-zmq-backend");
function cleanup(connectionFile) {
try {
fs_1.default.unlinkSync(connectionFile);
}
catch (e) {
return;
}
}
exports.cleanup = cleanup;
/**
* Creates a JupyterConnectionInfo object for a kernel given an array of comm
* channel ports
*
* @private
* @param ports array of comm channel ports to use for the connection,
* [hb_port, control_port, shell_port, stdin_port, iopub_port]
* @return JupyterConnectionInfo object
*/
function createConnectionInfo(ports) {
return {
version: 5,
key: uuid_1.default.v4(),
signature_scheme: "hmac-sha256",
transport: "tcp",
ip: "127.0.0.1",
hb_port: ports[0],
control_port: ports[1],
shell_port: ports[2],
stdin_port: ports[3],
iopub_port: ports[4]
};
}
/**
* Write a connection file
* @public
* @param [portFinderOptions] connection options
* see {@link https://github.com/indexzero/node-portfinder/blob/master/lib/portfinder.js }
* @param [portFinderOptions.port]
* @param [portFinderOptions.host]
* @return configResults
* @return configResults.config connection info
* @return configResults.connectionFile path to the connection file
*/
async function writeConnectionFile(portFinderOptions = {
host: "127.0.0.1",
port: 9000
}) {
const writeFile = util_1.default.promisify(fs_1.default.writeFile);
const getPorts = util_1.default.promisify(portfinder_1.getPorts);
try {
const ports = await getPorts(5, portFinderOptions);
const runtimeDir = await jupyter_paths_1.default.runtimeDir();
mkdirp_1.default(runtimeDir, error => {
if (error) {
throw error;
}
});
// Write the kernel connection file.
const config = createConnectionInfo(ports);
const connectionFile = path_1.default.join(await jupyter_paths_1.default.runtimeDir(), `kernel-${uuid_1.default.v4()}.json`);
await writeFile(connectionFile, config);
return {
config,
connectionFile
};
}
catch (error) {
console.log(error);
throw error;
}
}
/**
* Launch a kernel for a given kernelSpec
* @public
* @param kernelSpec describes a specific kernel
* @param [spawnOptions] `child_process`-like {@link https://github.com/sindresorhus/execa#options options for execa}
* use `{ cleanupConnectionFile: false }` to disable automatic connection file cleanup
*/
async function launchSpec(kernelSpec, spawnOptions) {
const info = await writeConnectionFile();
return launchSpecFromConnectionInfo(kernelSpec, info.config, info.connectionFile, spawnOptions);
}
exports.launchSpec = launchSpec;
/**
* Launch a kernel for a given kernelSpec and connection info
* @public
* @param kernelSpec describes a specific kernel, see the npm package
* `kernelspecs`
* @param config connection config
* @param connectionFile path to the config file
* @param [spawnOptions] `child_process`-like options for
* [execa]{@link https://github.com/sindresorhus/execa#options}
* use `{ cleanupConnectionFile: false }` to disable
* automatic connection file cleanup
*
* @return spawnResults
* @return spawnResults.spawn spawned process
* @return spawnResults.connectionFile connection file path
* @return spawnResults.config connection info
*
*/
async function launchSpecFromConnectionInfo(kernelSpec, config, connectionFile, spawnOptions) {
const argv = kernelSpec.argv.map((x) => x === "{connection_file}" ? connectionFile : x);
const defaultSpawnOptions = {
cleanupConnectionFile: true,
stdio: "ignore"
};
const env = Object.assign({}, process.env, kernelSpec.env);
const fullSpawnOptions = Object.assign({}, defaultSpawnOptions,
// TODO: see if this interferes with what execa assigns to the env option
{ env }, spawnOptions);
const runningKernel = execa_1.default(argv[0], argv.slice(1), fullSpawnOptions);
if (fullSpawnOptions.cleanupConnectionFile !== false) {
runningKernel.on("exit", (code, signal) => cleanup(connectionFile));
runningKernel.on("error", error => cleanup(connectionFile));
}
const channels = await enchannel_zmq_backend_1.createMainChannel(config);
return {
channels,
config,
connectionFile,
kernelSpec,
spawn: runningKernel
};
}
exports.launchSpecFromConnectionInfo = launchSpecFromConnectionInfo;
/**
* Launch a kernel by name
* @public
* @param kernelName
* @param [specs] array of kernelSpec objects to look through.
* See the npm package `kernelspecs`
* @param [spawnOptions] `child_process`-like options for
* [execa]{@link https://github.com/sindresorhus/execa#options}
* use `{ cleanupConnectionFile: false }` to disable
* automatic connection file cleanup
*/
async function launch(kernelName, spawnOptions, specs) {
// Let them pass in a cached specs file
if (!specs) {
const sp = await kernelspecs_1.findAll();
return launch(kernelName, spawnOptions, sp);
}
if (!specs[kernelName]) {
return Promise.reject(new Error(`No spec available for ${kernelName}`));
}
const spec = specs[kernelName].spec;
return launchSpec(spec, spawnOptions);
}
exports.launch = launch;