From 93a7827d6b23156fd22f18f12e1997ac242dfeff Mon Sep 17 00:00:00 2001 From: caven Date: Mon, 21 Sep 2026 17:04:43 +0800 Subject: [PATCH 1/3] fix(runtime): use project Python selector in source launcher Signed-off-by: caven --- scripts/loopx | 31 +++++++----- tests/test_loopx_launcher_python_selection.py | 49 +++++++++++++++++++ 2 files changed, 69 insertions(+), 11 deletions(-) create mode 100644 tests/test_loopx_launcher_python_selection.py diff --git a/scripts/loopx b/scripts/loopx index b5090097cb..6080d794a3 100755 --- a/scripts/loopx +++ b/scripts/loopx @@ -63,17 +63,26 @@ if [[ "$loopx_command" == "quota" \ exec node --no-warnings --experimental-strip-types "$native_scheduler_entry" "$@" fi -python_bin="${LOOPX_PYTHON:-}" -if [[ -z "$python_bin" && -f "$repo_root/.loopx-python" ]]; then - IFS= read -r python_bin <"$repo_root/.loopx-python" -fi -if [[ -z "$python_bin" ]]; then - python_bin="python3" -fi -if ! command -v "$python_bin" >/dev/null 2>&1; then - echo "loopx runtime error: configured Python executable not found: $python_bin" >&2 - echo "Set LOOPX_PYTHON to Python 3.11+ and reinstall LoopX." >&2 - exit 2 +python_selector="$repo_root/scripts/loopx-python.sh" +if [[ -f "$python_selector" ]]; then + if ! python_bin="$(bash "$python_selector")"; then + echo "Set LOOPX_PYTHON to Python 3.11+ and reinstall LoopX." >&2 + exit 2 + fi +else + # Keep old release snapshots runnable if the companion selector is absent. + python_bin="${LOOPX_PYTHON:-}" + if [[ -z "$python_bin" && -f "$repo_root/.loopx-python" ]]; then + IFS= read -r python_bin <"$repo_root/.loopx-python" + fi + if [[ -z "$python_bin" ]]; then + python_bin="python3" + fi + if ! command -v "$python_bin" >/dev/null 2>&1; then + echo "loopx runtime error: configured Python executable not found: $python_bin" >&2 + echo "Set LOOPX_PYTHON to Python 3.11+ and reinstall LoopX." >&2 + exit 2 + fi fi if [[ "$loopx_command" == "canary" && "$loopx_subcommand" == "premerge" ]]; then diff --git a/tests/test_loopx_launcher_python_selection.py b/tests/test_loopx_launcher_python_selection.py new file mode 100644 index 0000000000..57fb3908a4 --- /dev/null +++ b/tests/test_loopx_launcher_python_selection.py @@ -0,0 +1,49 @@ +from __future__ import annotations + +import os +from pathlib import Path +import shlex +import shutil +import subprocess +import sys + + +REPO_ROOT = Path(__file__).resolve().parents[1] + + +def _write_stub(path: Path, body: str) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(f"#!/usr/bin/env bash\n{body}\n", encoding="utf-8") + path.chmod(0o755) + + +def test_source_launcher_uses_project_venv_without_path_activation(tmp_path: Path) -> None: + release_root = tmp_path / "release" + scripts = release_root / "scripts" + scripts.mkdir(parents=True) + package = release_root / "loopx" + package.mkdir() + (package / "cli.py").write_text("print('launcher selected the project interpreter')\n", encoding="utf-8") + shutil.copy2(REPO_ROOT / "scripts/loopx", scripts / "loopx") + shutil.copy2(REPO_ROOT / "scripts/loopx-python.sh", scripts / "loopx-python.sh") + + selected = tmp_path / "selected-python.log" + _write_stub( + release_root / ".venv/bin/python", + "printf selected > " + f"{shlex.quote(str(selected))}\n" + f"exec {shlex.quote(sys.executable)} \"$@\"", + ) + + completed = subprocess.run( + ["bash", str(scripts / "loopx"), "version"], + cwd=release_root, + env={**os.environ, "PATH": "/usr/bin:/bin"}, + text=True, + capture_output=True, + check=False, + ) + + assert completed.returncode == 0, completed.stderr + assert selected.read_text(encoding="utf-8") == "selected" + assert "Python 3.11+ is required" not in completed.stderr From adfc9f24183d7b37ad426d7528bcc9808b65e905 Mon Sep 17 00:00:00 2001 From: caven Date: Tue, 22 Sep 2026 14:25:27 +0800 Subject: [PATCH 2/3] fix(runtime): preserve explicit Python diagnostics Signed-off-by: caven --- scripts/loopx | 9 ++++- tests/test_loopx_launcher_python_selection.py | 34 ++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/scripts/loopx b/scripts/loopx index 6080d794a3..faeb5b36f3 100755 --- a/scripts/loopx +++ b/scripts/loopx @@ -64,7 +64,14 @@ if [[ "$loopx_command" == "quota" \ fi python_selector="$repo_root/scripts/loopx-python.sh" -if [[ -f "$python_selector" ]]; then +if [[ -n "${LOOPX_PYTHON:-}" ]]; then + python_bin="$LOOPX_PYTHON" + if ! command -v "$python_bin" >/dev/null 2>&1; then + echo "loopx runtime error: configured Python executable not found: $python_bin" >&2 + echo "Set LOOPX_PYTHON to Python 3.11+ and reinstall LoopX." >&2 + exit 2 + fi +elif [[ -f "$python_selector" ]]; then if ! python_bin="$(bash "$python_selector")"; then echo "Set LOOPX_PYTHON to Python 3.11+ and reinstall LoopX." >&2 exit 2 diff --git a/tests/test_loopx_launcher_python_selection.py b/tests/test_loopx_launcher_python_selection.py index 57fb3908a4..852ebed0c1 100644 --- a/tests/test_loopx_launcher_python_selection.py +++ b/tests/test_loopx_launcher_python_selection.py @@ -38,7 +38,10 @@ def test_source_launcher_uses_project_venv_without_path_activation(tmp_path: Pat completed = subprocess.run( ["bash", str(scripts / "loopx"), "version"], cwd=release_root, - env={**os.environ, "PATH": "/usr/bin:/bin"}, + env={ + **{key: value for key, value in os.environ.items() if key != "LOOPX_PYTHON"}, + "PATH": "/usr/bin:/bin", + }, text=True, capture_output=True, check=False, @@ -47,3 +50,32 @@ def test_source_launcher_uses_project_venv_without_path_activation(tmp_path: Pat assert completed.returncode == 0, completed.stderr assert selected.read_text(encoding="utf-8") == "selected" assert "Python 3.11+ is required" not in completed.stderr + + +def test_source_launcher_preserves_explicit_missing_python_diagnostic( + tmp_path: Path, +) -> None: + release_root = tmp_path / "release" + scripts = release_root / "scripts" + scripts.mkdir(parents=True) + (release_root / "loopx").mkdir() + shutil.copy2(REPO_ROOT / "scripts/loopx", scripts / "loopx") + shutil.copy2(REPO_ROOT / "scripts/loopx-python.sh", scripts / "loopx-python.sh") + + missing = tmp_path / "missing-python" + completed = subprocess.run( + ["bash", str(scripts / "loopx"), "version"], + cwd=release_root, + env={ + **{key: value for key, value in os.environ.items() if key != "LOOPX_PYTHON"}, + "LOOPX_PYTHON": str(missing), + "PATH": "/usr/bin:/bin", + }, + text=True, + capture_output=True, + check=False, + ) + + assert completed.returncode == 2 + assert "configured Python executable not found" in completed.stderr + assert str(missing) in completed.stderr From 79cae08285cbf91d776e216e5a1e4dfe8247d10b Mon Sep 17 00:00:00 2001 From: caven Date: Tue, 22 Sep 2026 14:38:28 +0800 Subject: [PATCH 3/3] test(runtime): cover explicit Python compatibility failures Signed-off-by: caven --- tests/test_loopx_launcher_python_selection.py | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/test_loopx_launcher_python_selection.py b/tests/test_loopx_launcher_python_selection.py index 852ebed0c1..4800747bd6 100644 --- a/tests/test_loopx_launcher_python_selection.py +++ b/tests/test_loopx_launcher_python_selection.py @@ -79,3 +79,38 @@ def test_source_launcher_preserves_explicit_missing_python_diagnostic( assert completed.returncode == 2 assert "configured Python executable not found" in completed.stderr assert str(missing) in completed.stderr + + +def test_source_launcher_preserves_explicit_old_python_diagnostic( + tmp_path: Path, +) -> None: + release_root = tmp_path / "release" + scripts = release_root / "scripts" + scripts.mkdir(parents=True) + (release_root / "loopx").mkdir() + shutil.copy2(REPO_ROOT / "scripts/loopx", scripts / "loopx") + shutil.copy2(REPO_ROOT / "scripts/loopx-python.sh", scripts / "loopx-python.sh") + + legacy = tmp_path / "python-legacy" + _write_stub( + legacy, + "if [[ \"$1\" == \"-c\" ]]; then\n" + " printf '%s\\n' 'loopx runtime error: Python 3.11+ is required; selected Python is 3.9. Set LOOPX_PYTHON to Python 3.11+ and reinstall LoopX.' >&2\n" + "fi\n" + "exit 2", + ) + clean_env = { + key: value for key, value in os.environ.items() if key != "LOOPX_PYTHON" + } + + completed = subprocess.run( + ["bash", str(scripts / "loopx"), "version"], + cwd=release_root, + env={**clean_env, "LOOPX_PYTHON": str(legacy), "PATH": "/usr/bin:/bin"}, + text=True, + capture_output=True, + check=False, + ) + + assert completed.returncode == 2 + assert "selected Python is 3.9" in completed.stderr