UNPKG

@nteract/fs-kernels

Version:

A manager for the filesystem aspects of Juyter kernels

191 lines (190 loc) 7.08 kB
"use strict"; /** * 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;