node-calls-python
Version:
This module lets you run python code inside node without spawning new processes
267 lines (238 loc) • 6.9 kB
JavaScript
const { execSync } = require("child_process");
const fs = require("fs");
const path = require("path");
const nodecallspython = require("./build/Release/nodecallspython");
const chokidar = require("chokidar");
class Interpreter
{
loadPython(dir)
{
const debug = process.env.NODECALLSPYTHON_DEBUG !== undefined;
if (debug)
console.log("Loading python from " + dir);
let found = false;
if (fs.existsSync(dir))
{
fs.readdirSync(dir).forEach(file => {
if (file.match(/libpython3.*\.so/))
{
try
{
const filename = path.join(dir, file);
if (debug)
console.log("Running fixlink on " + filename);
this.fixlink(filename);
found = true;
}
catch(e)
{
console.error(e);
}
}
});
}
if (!found && debug)
console.log("Not found");
return found;
}
getPythonExecutableImpl(command)
{
const stdout = execSync(command);
if (stdout)
{
const result = stdout.toString().trim().split("\n");
if (result.length > 0)
return result[0].trim();
}
return undefined;
}
getPythonExecutable()
{
if (process.platform === "win32")
return this.getPythonExecutableImpl("where python");
else
return this.getPythonExecutableImpl("which python3");
}
constructor()
{
this.py = new nodecallspython.PyInterpreter();
if (process.platform === "linux")
{
const stdout = execSync("python3-config --configdir");
let found = false;
if (stdout)
{
const dir = stdout.toString().trim();
const res = this.loadPython(dir);
if (res)
found = true;
}
if (!found)
{
const stdout = execSync("python3-config --ldflags");
if (stdout)
{
const split = stdout.toString().trim().split(" ");
split.forEach(s => {
if (s.startsWith("-L"))
this.loadPython(s.substring(2));
});
}
}
}
try
{
this.setPythonExecutable(this.getPythonExecutable());
}
catch(e)
{
}
}
import(filename, allowReimport = false)
{
return new Promise(function(resolve, reject) {
try
{
this.py.import(filename, allowReimport, function(handler, error) {
if (handler)
resolve(handler);
else
reject(error);
});
}
catch(e)
{
reject(e);
}
}.bind(this));
}
importSync(filename, allowReimport = false)
{
return this.py.importSync(filename, allowReimport);
}
call(handler, func, ...args)
{
return new Promise(function(resolve, reject) {
try
{
this.py.call(handler, func, ...args, function(result, error) {
if (error)
reject(error);
else
resolve(result);
});
}
catch(e)
{
reject(e);
}
}.bind(this));
}
callSync(handler, func, ...args)
{
return this.py.callSync(handler, func, ...args);
}
create(handler, func, ...args)
{
return new Promise(function(resolve, reject) {
try
{
this.py.create(handler, func, ...args, function(result, error) {
if (error)
reject(error);
else
resolve(result);
});
}
catch(e)
{
reject(e);
}
}.bind(this));
}
createSync(handler, func, ...args)
{
return this.py.createSync(handler, func, ...args);
}
fixlink(filename)
{
return this.py.fixlink(filename);
}
reimport(directory)
{
return this.py.reimport(directory);
}
exec(handler, code)
{
return new Promise(function(resolve, reject) {
try
{
this.py.exec(handler, code, function(result, error) {
if (error)
reject(error);
else
resolve(result);
});
}
catch(e)
{
reject(e);
}
}.bind(this));
}
execSync(handler, code)
{
return this.py.execSync(handler, code);
}
eval(handler, code)
{
return new Promise(function(resolve, reject) {
try
{
this.py.eval(handler, code, function(result, error) {
if (error)
reject(error);
else
resolve(result);
});
}
catch(e)
{
reject(e);
}
}.bind(this));
}
evalSync(handler, code)
{
return this.py.evalSync(handler, code);
}
addImportPath(path)
{
return this.py.addImportPath(path);
}
setSyncJsAndPyInCallback(syncJsAndPy)
{
return this.py.setSyncJsAndPyInCallback(syncJsAndPy);
}
setPythonExecutable(executable)
{
const escaped = executable.trim().replace(/\\/g, '\\\\\\\\');
this.py.execSync({}, 'import multiprocessing; multiprocessing.set_executable(\'' + escaped + '\');');
}
developmentMode(paths)
{
const watcher = chokidar.watch(paths, {
persistent: true,
ignoreInitial: true,
});
watcher.on("change", (fileName) => {
const ext = path.extname(fileName);
if (ext == ".py" || ext == "py")
this.reimport(fileName);
});
}
}
let py = new Interpreter();
module.exports = {
interpreter: py
}