From 2cde2b6824edd3bdad247081c16ee075a92e9c98 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Thu, 21 Mar 2024 13:21:37 -0700 Subject: [PATCH] server: add support for versioned python env vars. only use portable python if env is not set. --- server/.vscode/launch.json | 3 +- server/package-lock.json | 4 +- server/src/plugin/runtime/python-worker.ts | 113 ++++++++++++--------- 3 files changed, 67 insertions(+), 53 deletions(-) diff --git a/server/.vscode/launch.json b/server/.vscode/launch.json index 358303953..31d273bbd 100644 --- a/server/.vscode/launch.json +++ b/server/.vscode/launch.json @@ -31,7 +31,8 @@ // "SCRYPTED_DEFAULT_AUTHENTICATION": "demo" // force usage of system python because brew python is 3.11 // which has no wheels for coreml tools or tflite-runtime - // "SCRYPTED_PYTHON_PATH": "/usr/bin/python3", + "SCRYPTED_PYTHON_PATH": "/opt/homebrew/bin/python3.11", + "SCRYPTED_PYTHON310_PATH": "/opt/homebrew/bin/python3.10", // "SCRYPTED_SHARED_WORKER": "true", // "SCRYPTED_DISABLE_AUTHENTICATION": "true", // "DEBUG": "*", diff --git a/server/package-lock.json b/server/package-lock.json index 5c475afa3..b07fe895f 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scrypted/server", - "version": "0.94.37", + "version": "0.94.38", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@scrypted/server", - "version": "0.94.37", + "version": "0.94.38", "hasInstallScript": true, "license": "ISC", "dependencies": { diff --git a/server/src/plugin/runtime/python-worker.ts b/server/src/plugin/runtime/python-worker.ts index 6da7fa980..ca052d8ca 100644 --- a/server/src/plugin/runtime/python-worker.ts +++ b/server/src/plugin/runtime/python-worker.ts @@ -13,14 +13,16 @@ import { RuntimeWorkerOptions } from "./runtime-worker"; export class PythonRuntimeWorker extends ChildProcessWorker { static { - try { - const py = new PortablePython(packagedPythonVersion); - const portablePython = py.executablePath; - // is this possible? - if (fs.existsSync(portablePython)) - process.env.SCRYPTED_PYTHON_PATH = portablePython; - } - catch (e) { + if (!fs.existsSync(process.env.SCRYPTED_PYTHON_PATH)) { + try { + const py = new PortablePython(packagedPythonVersion); + const portablePython = py.executablePath; + // is this possible? + if (fs.existsSync(portablePython)) + process.env.SCRYPTED_PYTHON_PATH = portablePython; + } + catch (e) { + } } } @@ -85,7 +87,7 @@ export class PythonRuntimeWorker extends ChildProcessWorker { } let pythonPath = process.env.SCRYPTED_PYTHON_PATH; - const pluginPythonVersion = options.packageJson.scrypted.pythonVersion?.[os.platform()]?.[os.arch()] || options.packageJson.scrypted.pythonVersion?.default; + const pluginPythonVersion: string = options.packageJson.scrypted.pythonVersion?.[os.platform()]?.[os.arch()] || options.packageJson.scrypted.pythonVersion?.default; if (!pythonPath) { if (os.platform() === 'win32') { @@ -114,53 +116,64 @@ export class PythonRuntimeWorker extends ChildProcessWorker { this.worker.stderr.pipe(this.stderr); }; - // if the plugin requests a specific python, then install it via portable python - if (pluginPythonVersion) { - const peerin = this.peerin = new PassThrough(); - const peerout = this.peerout = new PassThrough(); - - const finishSetup = () => { - setup(); - - peerin.pipe(this.worker.stdio[3] as Writable); - (this.worker.stdio[4] as Readable).pipe(peerout); - }; - - const pyVersion = require('py/package.json').version; - const pyPath = path.join(getPluginVolume(pluginId), 'py'); - const portableInstallPath = path.join(pyPath, pyVersion); - - const py = new PortablePython(pluginPythonVersion, portableInstallPath); - if (fs.existsSync(py.executablePath)) { - pythonPath = py.executablePath; - finishSetup(); - } - else { - (async () => { - try { - this.pythonInstallationComplete = false; - await fs.promises.rm(pyPath, { recursive: true, force: true }).catch(() => { }); - pythonPath = await installScryptedServerRequirements(pluginPythonVersion, portableInstallPath); - finishSetup(); - } - catch (e) { - process.nextTick(() => { - this.emit('error', new Error('Failed to install portable python.')); - }); - } - finally { - this.pythonInstallationComplete = true - } - })(); - } - } - else { + if (!pluginPythonVersion) { setup(); this.peerin = this.worker.stdio[3] as Writable; this.peerout = this.worker.stdio[4] as Readable; this.setupWorker(); + return; } + + const strippedPythonVersion = pluginPythonVersion.replace('.', ''); + const envPython = process.env[`SCRYPTED_PYTHON${strippedPythonVersion}_PATH`]; + if (fs.existsSync(envPython)) { + pythonPath = envPython; + setup(); + this.peerin = this.worker.stdio[3] as Writable; + this.peerout = this.worker.stdio[4] as Readable; + this.setupWorker(); + return; + } + + const peerin = this.peerin = new PassThrough(); + const peerout = this.peerout = new PassThrough(); + + const finishSetup = () => { + setup(); + + peerin.pipe(this.worker.stdio[3] as Writable); + (this.worker.stdio[4] as Readable).pipe(peerout); + }; + + const pyVersion = require('py/package.json').version; + const pyPath = path.join(getPluginVolume(pluginId), 'py'); + const portableInstallPath = path.join(pyPath, pyVersion); + + const py = new PortablePython(pluginPythonVersion, portableInstallPath); + if (fs.existsSync(py.executablePath)) { + pythonPath = py.executablePath; + finishSetup(); + } + else { + (async () => { + try { + this.pythonInstallationComplete = false; + await fs.promises.rm(pyPath, { recursive: true, force: true }).catch(() => { }); + pythonPath = await installScryptedServerRequirements(pluginPythonVersion, portableInstallPath); + finishSetup(); + } + catch (e) { + process.nextTick(() => { + this.emit('error', new Error('Failed to install portable python.')); + }); + } + finally { + this.pythonInstallationComplete = true + } + })(); + } + } setupRpcPeer(peer: RpcPeer): void {