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); } }); } }