fix(build/runtime): isolate bundled Python from the build/user site-packages

Two related fixes for the bundled embeddable/standalone Python:

- build-common.sh exports PYTHONNOUSERSITE=1 for the whole build. Without it,
  pip on a dev/CI machine that has a system Python sees transitive deps as
  'already satisfied' via the per-user site (~/.local, %APPDATA%\Python) and
  SKIPS bundling them (typing_extensions, urllib3, certifi, ...). The packaged
  app then crashes with ModuleNotFoundError on clean end-user machines while
  'working' on the build box. Clean CI runners have no user site, so this is a
  no-op there.
- python.ts sets PYTHONNOUSERSITE=1 in the spawned server env so a stray
  user-site package on an end-user machine can't shadow (or mask a gap in) the
  bundled deps. When packaged it also sets SLOPSMITH_SKIP_PLUGIN_INSTALL=1 so
  the server doesn't block startup doing runtime 'pip install' of heavy
  optional plugin deps (torch/whisperx/demucs) — that hung the backend past the
  readiness window on first launch. (Pairs with the slopsmith core change that
  honours that flag.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
byrongamatos
2026-06-18 00:25:09 -07:00
committed by Bret Mogilefsky
co-authored by Claude Opus 4.8
parent 2655bbe364
commit cf3c19a503
2 changed files with 19 additions and 0 deletions
+9
View File
@@ -8,6 +8,15 @@
set -euo pipefail set -euo pipefail
# Isolate the embeddable/standalone Python used for bundling from the BUILD
# machine's per-user site-packages. Without this, pip on a dev machine that has
# a system Python installed sees transitive deps as "already satisfied" via the
# user site (~/.local or %APPDATA%\Python) and SKIPS bundling them (e.g.
# typing_extensions, urllib3, certifi). The packaged app then crashes with
# ModuleNotFoundError on clean end-user machines while "working" on the build
# box. CI runners have no user site, so this is a no-op there.
export PYTHONNOUSERSITE=1
# Check if this is being sourced by a platform script # Check if this is being sourced by a platform script
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
echo "Error: build-common.sh should not be run directly" >&2 echo "Error: build-common.sh should not be run directly" >&2
+10
View File
@@ -546,6 +546,10 @@ export async function startPython(): Promise<void> {
XDG_CACHE_HOME: cacheBase, XDG_CACHE_HOME: cacheBase,
TORCH_HOME: process.env.TORCH_HOME || path.join(cacheBase, 'torch'), TORCH_HOME: process.env.TORCH_HOME || path.join(cacheBase, 'torch'),
HF_HOME: process.env.HF_HOME || path.join(cacheBase, 'huggingface'), HF_HOME: process.env.HF_HOME || path.join(cacheBase, 'huggingface'),
// Never read the end user's per-user site-packages — the bundle ships
// its own deps, and a stray user-site package must not shadow (or mask
// a gap in) them. Mirrors PYTHONNOUSERSITE=1 used at build time.
PYTHONNOUSERSITE: '1',
RESOURCESPATH: app.isPackaged RESOURCESPATH: app.isPackaged
? process.resourcesPath ? process.resourcesPath
: path.join(__dirname, '..', '..', 'resources'), : path.join(__dirname, '..', '..', 'resources'),
@@ -597,6 +601,12 @@ export async function startPython(): Promise<void> {
// Set PYTHONHOME for bundled Python on all platforms // Set PYTHONHOME for bundled Python on all platforms
if (app.isPackaged) { if (app.isPackaged) {
// Packaged builds ship plugins but must NOT block startup doing a
// runtime `pip install` of heavy optional plugin deps (torch/whisperx/
// demucs) — that hangs the backend past the readiness window on first
// launch. The slopsmith plugin loader honours this flag and loads such
// plugins degraded (their optional features stay off until deps exist).
pythonEnv.SLOPSMITH_SKIP_PLUGIN_INSTALL = '1';
if (process.platform === 'win32') { if (process.platform === 'win32') {
pythonEnv.PYTHONHOME = path.join(process.resourcesPath, 'python'); pythonEnv.PYTHONHOME = path.join(process.resourcesPath, 'python');
} else { } else {