From 8c361df192cb660f1afb677796de5ece9faeb799 Mon Sep 17 00:00:00 2001 From: AMagicPear Date: Wed, 20 May 2026 11:43:54 +0800 Subject: [PATCH] fix: support uv-managed Python in dev mode project creation On macOS, venv --copies fails with SIGABRT when ensurepip is invoked because copied Python binaries have hardcoded paths that resolve to the original venv location. Additionally, --copies on macOS copies the python executable but not libpython3.12.dylib, causing 'Library not loaded' at runtime since @executable_path/../lib/libpython3.12.dylib points to the original. Fix: use symlink mode (the default) instead of --copies, and bootstrap pip manually with 'pip install --target' after venv creation to avoid ensurepip entirely. Also add a fallback in _ensure_pip for the bundled runtime path: if ensurepip fails, copy pip from the host Python's site-packages. --- packaging/model/project_model.py | 14 +++++++- packaging/stage_bundled_python_runtime.py | 43 +++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/packaging/model/project_model.py b/packaging/model/project_model.py index 2c2b78ba..22fa8bed 100644 --- a/packaging/model/project_model.py +++ b/packaging/model/project_model.py @@ -370,10 +370,22 @@ def _create_project_runtime(self, project_dir: str, *, on_status=None) -> None: return # Dev mode: create a classic .venv + # Note: --copies is NOT used here. On macOS, venv --copies copies the Python + # executable but NOT libpython3.12.dylib, causing "Library not loaded" at runtime. + # Using symlinks instead: the python symlink resolves to the original binary, + # and @executable_path/../lib/libpython3.12.dylib resolves correctly from there. venv_path = os.path.join(project_dir, ".venv") if on_status: on_status("Creating project virtual environment...") - _run_hidden([sys.executable, "-m", "venv", "--copies", venv_path], timeout=600) + _run_hidden([sys.executable, "-m", "venv", venv_path], timeout=600) + + # Bootstrap pip into the venv using the host Python (avoids ensurepip issues). + venv_lib = os.path.join(venv_path, "lib", f"python{sys.version_info.major}.{sys.version_info.minor}", "site-packages") + _run_hidden( + [sys.executable, "-m", "pip", "install", "--no-input", "--disable-pip-version-check", + "--target", venv_lib, "pip"], + timeout=300, + ) def _install_infernux_in_runtime(self, project_dir: str, engine_version: str = "", *, on_status=None): """Install the Infernux wheel into the project's Python environment. diff --git a/packaging/stage_bundled_python_runtime.py b/packaging/stage_bundled_python_runtime.py index 4fd7a5f7..2e66f4f9 100644 --- a/packaging/stage_bundled_python_runtime.py +++ b/packaging/stage_bundled_python_runtime.py @@ -304,6 +304,49 @@ def _ensure_pip(python_exe: str) -> None: if completed.returncode == 0: return + # ensurepip can fail with SIGABRT on copied Python binaries on macOS. + # Try a fallback: copy pip from the original venv that we know works. + import shutil as _shutil, os as _os, sys as _sys, subprocess as _subprocess + original_python = _sys.executable + if original_python and _os.path.dirname(original_python) != _os.path.dirname(python_exe): + import site as _site + orig_site_packages = next( + (p for p in _site.getsitepackages() if _os.path.isdir(p) and "site-packages" in p), None + ) + target_site_packages = next( + (p for p in _site.getsitepackages() if _os.path.isdir(p) and "site-packages" in p), None + ) if False else None + # Try to find target site-packages via python path + try: + result = _subprocess.run( + [python_exe, "-c", "import site; print(site.getsitepackages()[0])"], + capture_output=True, text=True, timeout=10 + ) + target_site_packages = result.stdout.strip() if result.returncode == 0 else None + except Exception: + target_site_packages = None + + if orig_site_packages and target_site_packages: + pip_src = _os.path.join(orig_site_packages, "pip") + if _os.path.isdir(pip_src): + pip_dest = _os.path.join(target_site_packages, "pip") + try: + _shutil.rmtree(pip_dest, ignore_errors=True) + _shutil.copytree(pip_src, pip_dest) + # Also copy wheel & certifi dependencies if present + for dep in ("wheel", "certifi", "distlib"): + dep_src = _os.path.join(orig_site_packages, dep) + if _os.path.isdir(dep_src): + _shutil.rmtree(_os.path.join(target_site_packages, dep), ignore_errors=True) + _shutil.copytree(dep_src, _os.path.join(target_site_packages, dep)) + # Verify it works now + verify = _subprocess.run([python_exe, "-m", "pip", "--version"], capture_output=True, timeout=30) + if verify.returncode == 0: + return + except Exception: + pass + + # Last resort: try ensurepip normally completed = _run([python_exe, "-m", "ensurepip", "--upgrade"], timeout=600) if completed.returncode != 0: raise SystemExit(