diff --git a/go_server/main.go b/go_server/main.go index ebd93f4d..67e8753a 100644 --- a/go_server/main.go +++ b/go_server/main.go @@ -41,6 +41,7 @@ func main() { Utils.CreateHandleFunc("get_server_health", handleGetServerHealth) Utils.CreateHandleFunc("removeId/", handleRemoveId) Utils.CreateHandleFunc("clearAll", handleClearAll) + Utils.CreateHandleFunc("update_python_env", handleUpdatePythonEnv) // We check if the conda environment was passed as an argument condaEnv := os.Getenv("MED_ENV") @@ -97,6 +98,24 @@ func convScript2JsonStr(script Utils.ScriptInfo) (string, error) { return jsonData, nil } +// handleUpdatePythonEnv updates the python environment (MED_ENV) used by StartPythonScripts. +func handleUpdatePythonEnv(jsonConfig string, id string) (string, error) { + config, err := Utils.JsonStr2map(jsonConfig) + if err != nil { + return "", err + } + pythonPath, ok := config["pythonPath"].(string) + if !ok || pythonPath == "" { + return "", fmt.Errorf("no pythonPath provided") + } + if _, err := os.Stat(pythonPath); err != nil { + return "", fmt.Errorf("python executable not found at: %s", pythonPath) + } + os.Setenv("MED_ENV", pythonPath) + log.Println("Python environment updated to: " + pythonPath) + return "Python environment set to " + pythonPath, nil +} + // handleRemoveId handles the request to remove the id from the scripts func handleRemoveId(jsonConfig string, id string) (string, error) { ok := Utils.KillScript(id) diff --git a/main/background.js b/main/background.js index d3579e32..c4378d29 100644 --- a/main/background.js +++ b/main/background.js @@ -299,9 +299,23 @@ if (isProd) { console.log("process.resourcesPath: ", process.resourcesPath) console.log(MEDconfig.runServerAutomatically ? "Server will start automatically here (in background of the application)" : "Server must be started manually") let bundledPythonPath = getBundledPythonEnvironment() + // The user-defined python path (condaPath in settings.json) takes precedence over the bundled python + let resolvedPythonPath = bundledPythonPath + try { + const settingsFilePath = path.join(app.getPath("userData"), "settings.json") + if (fs.existsSync(settingsFilePath)) { + const savedSettings = JSON.parse(fs.readFileSync(settingsFilePath, "utf8")) + if (savedSettings.condaPath) { + resolvedPythonPath = savedSettings.condaPath + } + } + } catch (error) { + console.warn("Could not read settings.json to resolve the python path: ", error) + } + console.log("Python path passed to the server: ", resolvedPythonPath) if (MEDconfig.runServerAutomatically) { // Start the Go server – Python path is optional (passed if available) - runServer(isProd, serverPort, serverProcess, serverState, bundledPythonPath) + runServer(isProd, serverPort, serverProcess, serverState, resolvedPythonPath) .then((process) => { serverProcess = process console.log("Server process started: ", serverProcess) diff --git a/renderer/components/mainPages/settings.jsx b/renderer/components/mainPages/settings.jsx index 64bbafd8..976e4cfa 100644 --- a/renderer/components/mainPages/settings.jsx +++ b/renderer/components/mainPages/settings.jsx @@ -17,6 +17,7 @@ import { Column } from "primereact/column" import { WorkspaceContext } from "../workspace/workspaceContext" import FirstSetupModal from "../generalPurpose/installation/firstSetupModal" import { requestBackend } from "../../utilities/requests" +import { cond } from "lodash" const util = require("util") const exec = util.promisify(require("child_process").exec) @@ -74,16 +75,27 @@ const SettingsPage = ({pageId = "settings", checkJupyterIsRunning, startJupyterS checkServer() checkMongoIsRunning() getJupyterStatus() + let condaCustomPath = null ipcRenderer.invoke("get-settings").then((receivedSettings) => { console.log("received settings", receivedSettings) setSettings(receivedSettings) - if (receivedSettings?.condaPath) { + if (receivedSettings?.condaPath && receivedSettings?.condaPath !== condaPath) { + condaCustomPath = receivedSettings?.condaPath setCondaPath(receivedSettings?.condaPath) } if (receivedSettings?.seed) { setSeed(receivedSettings?.seed) } }) + ipcRenderer.invoke("getBundledPythonEnvironment").then((res) => { + console.log("Python embedded: ", res) + if (res !== null && !condaCustomPath) { + ipcRenderer.invoke("getInstalledPythonPackages", res).then((pythonPackages) => { + console.log("Installed Python Packages: ", pythonPackages) + setPythonEmbedded({ pythonEmbedded: res, pythonPackages: pythonPackages }) + }) + } + }) }, []) /** @@ -111,6 +123,31 @@ const SettingsPage = ({pageId = "settings", checkJupyterIsRunning, startJupyterS }, 1000) } + /** + * Notify the running Go server that the python environment changed (updates its MED_ENV), + * so that python scripts launched after this call use the new interpreter + * @param {String} newPath - Path to the python executable + * @returns {void} + */ + const updatePythonEnvOnServer = (newPath) => { + if (!newPath) return + requestBackend( + port, + "update_python_env", + { pythonPath: newPath, pageId: pageId }, + (data) => { + if (data?.error) { + console.warn("Python environment update rejected by the server: ", data.error) + } else { + console.log("Python environment update response: ", data) + } + }, + (error) => { + console.error("Failed to update the python environment on the server: ", error) + } + ) + } + /** * Check if the server is running every 5 seconds */ @@ -126,27 +163,22 @@ const SettingsPage = ({pageId = "settings", checkJupyterIsRunning, startJupyterS ipcRenderer.invoke("getBundledPythonEnvironment").then((res) => { console.log("Python embedded: ", res) - if (res !== null) { + if (res !== null && res !== pythonEmbedded && !condaPath) { ipcRenderer.invoke("getInstalledPythonPackages", res).then((pythonPackages) => { console.log("Installed Python Packages: ", pythonPackages) setPythonEmbedded({ pythonEmbedded: res, pythonPackages: pythonPackages }) }) - } + } + else if (condaPath && condaPath !== pythonEmbedded?.pythonEmbedded) { + setPythonEmbedded({...pythonEmbedded, pythonEmbedded:condaPath} ) + } }) }, 5000) return () => clearInterval(interval) }) useEffect(() => { - ipcRenderer.invoke("getBundledPythonEnvironment").then((res) => { - console.log("Python embedded: ", res) - if (res !== null) { - ipcRenderer.invoke("getInstalledPythonPackages", res).then((pythonPackages) => { - console.log("Installed Python Packages: ", pythonPackages) - setPythonEmbedded({ pythonEmbedded: res, pythonPackages: pythonPackages }) - }) - } - }) + }, []) const getJupyterStatus = async () => { @@ -317,6 +349,10 @@ const SettingsPage = ({pageId = "settings", checkJupyterIsRunning, startJupyterS onChange={(e) => { setCondaPath(e.target.value) saveSettings({ ...settings, condaPath: e.target.value }) + clearTimeout(window.updatePythonEnvTimeout) + window.updatePythonEnvTimeout = setTimeout(() => { + updatePythonEnvOnServer(e.target.value) + }, 1000) }} /> { console.log("path", path) setCondaPath(path) + setPythonEmbedded({...pythonEmbedded, pythonEmbedded:path} ) saveSettings({ ...settings, condaPath: path }) + updatePythonEnvOnServer(path) }) }} >