First commit.

This commit is contained in:
2025-11-18 11:02:39 +01:00
parent e57266f93d
commit d4c4ccfc18
16 changed files with 874 additions and 1 deletions

184
python_bindings.js Normal file
View File

@@ -0,0 +1,184 @@
import { PythonShell } from "python-shell";
export default class Bindings {
constructor(initialProps) {
this.pythonShell = new PythonShell("./python/index.py", { mode: "text" });
this.responseQueue = new Array();
this.sendQueue = new Array();
this.isSending = false;
this.streamCallback = null;
this.pythonShell.on("message", function(message) {
if (typeof message === "string" && message.startsWith("__STREAM__")) {
var dataStr = message.substring(10);
var data = null;
try {
data = JSON.parse(dataStr);
} catch (err) {
console.error("Invalid streamed JSON:", dataStr);
return;
}
if (this.streamCallback) {
this.streamCallback(data);
}
return;
}
var response = null;
try {
response = JSON.parse(message);
} catch (err) {
console.error("Invalid JSON response:", message);
var resolve = this.responseQueue.shift();
if (resolve) {
resolve({ error: "Invalid JSON from Python" });
}
return;
}
var resolve = this.responseQueue.shift();
if (resolve) {
resolve(response);
}
this.processSendQueue();
}.bind(this));
this.pythonShell.on("error", function(err) {
console.error("PythonShell error:", err);
});
this.pythonShell.on("stderr", function(stderr) {
console.error("PythonShell stderr:", stderr);
});
// Set initial properties if any
if (initialProps && typeof initialProps === "object") {
var keys = Object.keys(initialProps);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var value = initialProps[key];
this.callMethod("setProperty", key, value);
}
}
return new Proxy(this, {
get: function(target, prop) {
if (typeof prop === "string") {
if (prop === "end") {
return target.end.bind(target);
}
if (prop === "onMessage") {
return target.onMessage.bind(target);
}
return function() {
var args = new Array(arguments.length);
for (var i = 0; i < arguments.length; i++) {
args[i] = arguments[i];
}
return target.callMethod(prop, ...args);
};
}
return undefined;
},
set: function(target, prop, value) {
return target.callMethod("setProperty", prop, value)
.then(function() {
return true;
})
.catch(function(err) {
console.error(err);
return false;
});
}
});
}
onMessage(callback) {
this.streamCallback = callback;
}
callMethod(method) {
var args = new Array(arguments.length - 1);
for (var i = 1; i < arguments.length; i++) {
args[i - 1] = arguments[i];
}
return new Promise(function(resolve, reject) {
this.responseQueue.push(function(response) {
if (response.error) {
reject(new Error(response.error));
} else {
resolve(response.result !== undefined ? response.result : response);
}
});
var params = {};
if (method === "setProperty" && args.length === 2) {
params = { name: args[0], value: args[1] };
} else if (args.length === 1 && typeof args[0] === "object") {
params = args[0];
} else if (args.length > 0) {
params = { args: args };
}
this.sendQueue.push({ method: method, params: params });
this.processSendQueue();
}.bind(this));
}
processSendQueue() {
if (this.isSending === true) {
return;
}
if (this.sendQueue.length === 0) {
return;
}
var message = this.sendQueue.shift();
this.isSending = true;
try {
this.pythonShell.send(JSON.stringify(message));
} catch (err) {
var resolve = this.responseQueue.shift();
if (resolve) {
resolve({ error: err.message });
}
}
this.isSending = false;
}
end() {
this.pythonShell.end(function(err) {
if (err !== undefined && err !== null) {
console.error("Error ending PythonShell:", err);
}
});
}
}