jupyter-paths
Version:
Node wrapping of jupyter/jupyter_core to resolve paths across Jupyter installations.
241 lines (212 loc) • 6.07 kB
JavaScript
/**
* @module jupyter-paths
*
* @description Module `jupyter-paths` provides path helpers for Jupyter 4.x
*/
const exec = require("child_process").exec;
const fs = require("fs");
const path = require("path");
const home = require("home-dir");
let sysPrefixGuess = undefined;
function accessCheck(d) {
// check if a directory exists and is listable (X_OK)
if (!fs.existsSync(d)) return false;
try {
fs.accessSync(d, fs.X_OK);
} catch (e) {
// [WSA]EACCES
return false;
}
return true;
}
function guessSysPrefix() {
// inexpensive guess for sysPrefix based on location of `which python`
// based on shutil.which from Python 3.5
// only run once:
if (sysPrefixGuess !== undefined) return sysPrefixGuess;
const PATH = (process.env.PATH || "").split(path.delimiter);
if (PATH.length === 0) {
sysPrefixGuess = null;
return;
}
let pathext = [""];
if (process.platform === "win32") {
pathext = (process.env.PATHEXT || "").split(path.delimiter);
}
PATH.some((bin) => {
bin = path.resolve(bin);
const python = path.join(bin, "python");
return pathext.some((ext) => {
const exe = python + ext;
if (accessCheck(exe)) {
// PREFIX/bin/python exists, return PREFIX
// following symlinks
if (process.platform === "win32") {
// Windows: Prefix\Python.exe
try {
sysPrefixGuess = path.dirname(path.resolve(exe));
} catch {
sysPrefixGuess = null;
}
} else {
// Everywhere else: prefix/bin/python
sysPrefixGuess = path.dirname(path.dirname(path.resolve(exe)));
}
return true;
}
});
});
if (sysPrefixGuess === undefined) {
// store null as nothing found, but don't run again
sysPrefixGuess = null;
}
return sysPrefixGuess;
}
let askJupyterPromise = null;
function askJupyter() {
// ask Jupyter where the paths are
if (!askJupyterPromise) {
askJupyterPromise = new Promise((resolve, reject) => {
exec("jupyter --paths --json", (err, stdout) => {
if (err) {
console.warn("Failed to ask Jupyter about its paths: " + err);
reject(err);
} else {
resolve(JSON.parse(stdout.toString().trim()));
}
});
});
}
return askJupyterPromise;
}
function systemConfigDirs() {
const paths = [];
// System wide for Windows and Unix
if (process.platform === "win32") {
paths.push(path.resolve(path.join(process.env.PROGRAMDATA, "jupyter")));
} else {
paths.push("/usr/local/etc/jupyter");
paths.push("/etc/jupyter");
}
return paths;
}
function configDirs(opts) {
if (opts && opts.askJupyter) {
return askJupyter()
.then((paths) => paths.config)
.catch((err) => configDirs());
}
const paths = [];
if (process.env.JUPYTER_CONFIG_DIR) {
paths.push(process.env.JUPYTER_CONFIG_DIR);
}
paths.push(home(".jupyter"));
const systemDirs = systemConfigDirs();
if (opts && opts.withSysPrefix) {
return new Promise((resolve, reject) => {
// deprecated: withSysPrefix expects a Promise
// but no change in content
resolve(configDirs());
});
}
// inexpensive guess, based on location of `python` executable
const sysPrefix = guessSysPrefix();
const sysPathed = path.join(sysPrefix, "etc", "jupyter");
if (systemDirs.indexOf(sysPathed) === -1) {
paths.push(sysPathed);
}
return paths.concat(systemDirs);
}
function systemDataDirs() {
const paths = [];
// System wide for Windows and Unix
if (process.platform === "win32") {
paths.push(path.resolve(path.join(process.env.PROGRAMDATA, "jupyter")));
} else {
paths.push("/usr/local/share/jupyter");
paths.push("/usr/share/jupyter");
}
return paths;
}
/**
* where the userland data directory resides
* includes things like the runtime files
* @return {string} directory for data
*/
function userDataDir() {
// Userland specific
if (process.platform === "darwin") {
return home("Library/Jupyter");
} else if (process.platform === "win32") {
return path.resolve(path.join(process.env.APPDATA, "jupyter"));
}
// TODO: respect XDG_DATA_HOME
return home(".local/share/jupyter");
}
/**
* dataDirs returns all the expected static directories for this OS.
* The user of this function should make sure to make sure the directories
* exist.
*
* When withSysPrefix is set, this returns a promise of directories
*
* @param {bool} withSysPrefix include the sys.prefix paths
* @return {Array} All the Jupyter Data Dirs
*/
function dataDirs(opts) {
if (opts && opts.askJupyter) {
return (
askJupyter()
.then((paths) => paths.data)
// fallback on default
.catch((err) => dataDirs())
);
}
const paths = [];
if (process.env.JUPYTER_PATH) {
paths.push(process.env.JUPYTER_PATH);
}
paths.push(userDataDir());
const systemDirs = systemDataDirs();
if (opts && opts.withSysPrefix) {
return new Promise((resolve, reject) => {
// deprecated: withSysPrefix expects a Promise
// but no change in content
resolve(dataDirs());
});
}
// inexpensive guess, based on location of `python` executable
try {
const sysPrefix = guessSysPrefix();
if (sysPrefix) {
const sysPathed = path.join(sysPrefix, "share", "jupyter");
if (systemDirs.indexOf(sysPathed) === -1) {
paths.push(sysPathed);
}
}
} catch {}
return paths.concat(systemDirs);
}
function runtimeDir(opts) {
if (opts && opts.askJupyter) {
return (
askJupyter()
.then((paths) => paths.runtime)
// fallback on default
.catch((err) => runtimeDir())
);
}
if (process.env.JUPYTER_RUNTIME_DIR) {
return process.env.JUPYTER_RUNTIME_DIR;
}
if (process.env.XDG_RUNTIME_DIR) {
return path.join(process.env.XDG_RUNTIME_DIR, "jupyter");
}
return path.join(userDataDir(), "runtime");
}
module.exports = {
dataDirs,
runtimeDir,
configDirs,
askJupyter
};