Skip to content

Commit 7c2fef1

Browse files
committed
test: remove registry check from multiverse healing; unnecessary
Modified: • src/tests/test_multiverse_healing.py (+82/-98 lines) [gitship-generated]
1 parent c7b9dd9 commit 7c2fef1

1 file changed

Lines changed: 73 additions & 89 deletions

File tree

src/tests/test_multiverse_healing.py

Lines changed: 73 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
"""
22
Multiverse healing test — uses direct versioned binaries (8pkg39, 8pkg311, etc.)
3-
and reads interpreter paths from the omnipkg registry.
4-
No swap needed, works in CI across shells.
3+
No pre-flight registry check needed: the shim auto-adopts the interpreter and
4+
installs packages on first use. We only read the registry AFTER the shim has
5+
run (adoption guaranteed complete) to get the exe path for payload subprocesses.
56
"""
67
import sys
78
import os
@@ -19,93 +20,83 @@
1920
from omnipkg.i18n import _
2021

2122

22-
# --- REGISTRY HELPERS ---
23+
# --- HELPERS ---
2324

24-
def get_registry(venv_root: Path) -> dict:
25-
"""Read the omnipkg interpreter registry."""
26-
registry_path = venv_root / ".omnipkg" / "interpreters" / "registry.json"
27-
if not registry_path.exists():
28-
raise RuntimeError(f"Registry not found: {registry_path}")
29-
return json.loads(registry_path.read_text(encoding="utf-8"))
30-
31-
32-
def get_interpreter(venv_root: Path, version: str) -> Path:
33-
"""Get the python executable path for a given version from the registry."""
34-
registry = get_registry(venv_root)
35-
interpreters = registry.get("interpreters", {})
36-
if version not in interpreters:
37-
raise RuntimeError(
38-
f"Python {version} not found in registry. "
39-
f"Available: {list(interpreters.keys())}"
40-
)
41-
exe = Path(interpreters[version])
42-
if not exe.exists():
43-
raise RuntimeError(f"Interpreter path does not exist: {exe}")
44-
return exe
25+
def detect_venv_root() -> Path:
26+
"""Detect the omnipkg venv root from env or config."""
27+
override = os.environ.get("OMNIPKG_VENV_ROOT")
28+
if override:
29+
return Path(override)
30+
try:
31+
from omnipkg.core import ConfigManager
32+
cm = ConfigManager(suppress_init_messages=True)
33+
return cm.venv_path
34+
except Exception:
35+
pass
36+
raise RuntimeError("Could not determine OMNIPKG venv root. Set OMNIPKG_VENV_ROOT.")
4537

4638

4739
def get_versioned_bin(venv_root: Path, version: str, cmd: str) -> list:
4840
"""
49-
Return a command prefix for a versioned omnipkg invocation.
50-
51-
Strategy (in order):
52-
1. 8pkg.exe --python X.Y — preferred; exe is directly executable,
53-
no shell needed, works in subprocess.
54-
2. cmd /c 8pkgXY.bat — fallback for Windows when only .bat exists;
55-
.bat files require cmd.exe to interpret them
56-
and CANNOT be launched via CreateProcess directly.
57-
3. 8pkgXY (no extension) — Unix shim / symlink.
58-
59-
Returns a list so callers can do: run(get_versioned_bin(...) + ["install", ...])
41+
Return a command prefix for a versioned omnipkg shim.
42+
The shim handles auto-adopt + install — no registry pre-check needed.
43+
44+
Priority:
45+
1. cmd.exe --python X.Y (cross-platform, no shell)
46+
2. cmd /c cmdXY.bat (Windows .bat shim)
47+
3. cmdXY (Unix symlink/shim)
48+
4. cmd --python X.Y (plain Unix binary with flag)
6049
"""
61-
ver_tag = version.replace(".", "") # "3.9" -> "39"
50+
ver_tag = version.replace(".", "")
6251
is_windows = sys.platform == "win32"
6352

6453
for bin_dir in [venv_root / "Scripts", venv_root / "bin"]:
65-
# --- Prefer the plain exe with --python flag (cross-platform, no shell needed) ---
54+
# Plain .exe with --python flag — works everywhere without shell
6655
exe = bin_dir / f"{cmd}.exe"
6756
if exe.exists():
6857
return [str(exe), "--python", version]
6958

70-
plain = bin_dir / cmd
71-
if plain.exists() and not is_windows:
72-
return [str(plain), "--python", version]
73-
74-
# --- .bat fallback: must be wrapped in cmd /c on Windows ---
59+
# Windows .bat shim — must go through cmd /c
7560
for bat_suffix in [f"{cmd}{ver_tag}.bat", f"{cmd}{ver_tag}.cmd"]:
7661
bat = bin_dir / bat_suffix
7762
if bat.exists():
78-
if is_windows:
79-
return ["cmd", "/c", str(bat)]
80-
else:
81-
# Should not happen, but handle gracefully
82-
return [str(bat)]
63+
return (["cmd", "/c", str(bat)] if is_windows else [str(bat)])
8364

84-
# --- Unix shim with no extension ---
65+
# Unix versioned shim (symlink like 8pkg39)
8566
shim = bin_dir / f"{cmd}{ver_tag}"
8667
if shim.exists():
8768
return [str(shim)]
8869

89-
raise RuntimeError(
90-
f"Could not find '{cmd}' binary for Python {version} in {venv_root}.\n"
91-
f" Looked for: {cmd}.exe, {cmd}, {cmd}{ver_tag}.bat, {cmd}{ver_tag}.cmd, {cmd}{ver_tag}\n"
92-
f" Under: {venv_root / 'Scripts'} and {venv_root / 'bin'}"
93-
)
70+
# Plain Unix binary with --python flag
71+
plain = bin_dir / cmd
72+
if plain.exists() and not is_windows:
73+
return [str(plain), "--python", version]
9474

75+
# Last resort: rely on PATH (CI often has 8pkg39 on PATH directly)
76+
shim_name = f"{cmd}{ver_tag}"
77+
safe_print(f" [WARN] No shim found under {venv_root} — falling back to PATH: {shim_name}")
78+
return [shim_name]
9579

96-
def detect_venv_root() -> Path:
97-
"""Detect the omnipkg venv root from env or config."""
98-
override = os.environ.get("OMNIPKG_VENV_ROOT")
99-
if override:
100-
return Path(override)
101-
# Fall back to reading config next to this python
102-
try:
103-
from omnipkg.core import ConfigManager
104-
cm = ConfigManager(suppress_init_messages=True)
105-
return cm.venv_path
106-
except Exception:
107-
pass
108-
raise RuntimeError("Could not determine OMNIPKG venv root. Set OMNIPKG_VENV_ROOT.")
80+
81+
def get_interpreter_after_adopt(venv_root: Path, version: str) -> Path:
82+
"""
83+
Read the interpreter exe from the registry.
84+
Only call this AFTER the versioned shim has already run (adoption complete).
85+
"""
86+
registry_path = venv_root / ".omnipkg" / "interpreters" / "registry.json"
87+
if not registry_path.exists():
88+
raise RuntimeError(f"Registry not found at {registry_path}")
89+
registry = json.loads(registry_path.read_text(encoding="utf-8"))
90+
interpreters = registry.get("interpreters", {})
91+
if version not in interpreters:
92+
raise RuntimeError(
93+
f"Python {version} still not in registry after adoption. "
94+
f"Available: {list(interpreters.keys())}"
95+
)
96+
exe = Path(interpreters[version])
97+
if not exe.exists():
98+
raise RuntimeError(f"Interpreter path does not exist: {exe}")
99+
return exe
109100

110101

111102
# --- SUBPROCESS HELPER ---
@@ -117,7 +108,6 @@ def run(cmd, description, check=True, env=None):
117108
_env = os.environ.copy()
118109
if env:
119110
_env.update(env)
120-
# Prevent recursive heal loops in subprocesses
121111
_env["OMNIPKG_DISABLE_AUTO_ALIGN"] = "1"
122112
_env["OMNIPKG_SUBPROCESS_MODE"] = "1"
123113

@@ -133,16 +123,12 @@ def run(cmd, description, check=True, env=None):
133123
env=_env,
134124
)
135125
except (OSError, FileNotFoundError) as exc:
136-
# Happens when the executable itself can't be launched (e.g. .bat without cmd /c)
137126
safe_print(f" [LAUNCH ERROR] Could not start process: {exc}")
138127
safe_print(f" Attempted executable: {cmd[0]!r}")
139-
safe_print(f" Full command: {cmd}")
140128
if check:
141129
raise RuntimeError(
142130
f"Failed to launch '{cmd[0]}': {exc}\n"
143-
f" Full command: {cmd}\n"
144-
f" Hint: on Windows, .bat files cannot be launched directly via "
145-
f"subprocess — they must be wrapped with ['cmd', '/c', ...]"
131+
f" Hint: on Windows, .bat files must be wrapped with ['cmd', '/c', ...]"
146132
) from exc
147133
return ""
148134

@@ -155,20 +141,16 @@ def run(cmd, description, check=True, env=None):
155141
proc.stdout.close()
156142
rc = proc.wait()
157143
output = "".join(lines)
158-
159144
safe_print(f" exit code: {rc}")
160145

161146
if check and rc != 0:
162-
safe_print(f"\n [FAILURE DETAILS] Command exited with code {rc}")
163-
safe_print(f" Executable : {cmd[0]!r}")
164-
safe_print(f" Full cmd : {' '.join(str(c) for c in cmd)}")
147+
safe_print(f"\n [FAILURE DETAILS] rc={rc}")
148+
safe_print(f" Full cmd: {' '.join(str(c) for c in cmd)}")
165149
if output.strip():
166-
safe_print(" --- captured output ---")
167150
for line in output.splitlines():
168151
safe_print(f" {line}")
169-
safe_print(" --- end output ---")
170152
else:
171-
safe_print(" (no output captured — process may have failed to start)")
153+
safe_print(" (no output captured)")
172154
raise subprocess.CalledProcessError(rc, cmd, output=output)
173155

174156
return output
@@ -206,19 +188,19 @@ def multiverse_analysis():
206188
venv_root = detect_venv_root()
207189
safe_print(f"[INFO] venv root: {venv_root}")
208190

209-
registry = get_registry(venv_root)
210-
safe_print(f"[INFO] available interpreters: {list(registry['interpreters'].keys())}")
211-
212191
# === STEP 1: Python 3.9 — legacy scipy/numpy ===
213-
safe_print("\n[STEP 1] Python 3.9 — installing legacy packages...")
214-
192+
# The shim handles adopt + install automatically — no registry pre-check.
193+
safe_print("\n[STEP 1] Python 3.9 — installing legacy packages via shim (auto-adopts)...")
215194
pkg39_cmd = get_versioned_bin(venv_root, "3.9", "8pkg")
216-
py39 = get_interpreter(venv_root, "3.9")
217195
safe_print(f"[INFO] pkg39 cmd prefix: {pkg39_cmd}")
218196

219197
run(pkg39_cmd + ["install", "numpy<2", "scipy"],
220198
"Installing numpy<2 + scipy into Python 3.9")
221199

200+
# Now adoption is complete — safe to read registry for the exe path
201+
py39 = get_interpreter_after_adopt(venv_root, "3.9")
202+
safe_print(f"[INFO] py39 exe: {py39}")
203+
222204
safe_print("\n[STEP 1] Running legacy payload in Python 3.9...")
223205
result = subprocess.run(
224206
[str(py39), __file__, "--run-legacy"],
@@ -237,15 +219,17 @@ def multiverse_analysis():
237219
safe_print(f"[OK] Legacy result: {legacy_data}")
238220

239221
# === STEP 2: Python 3.11 — modern tensorflow ===
240-
safe_print("\n[STEP 2] Python 3.11 — installing tensorflow...")
241-
222+
safe_print("\n[STEP 2] Python 3.11 — installing tensorflow via shim (auto-adopts)...")
242223
pkg311_cmd = get_versioned_bin(venv_root, "3.11", "8pkg")
243-
py311 = get_interpreter(venv_root, "3.11")
244224
safe_print(f"[INFO] pkg311 cmd prefix: {pkg311_cmd}")
245225

246226
run(pkg311_cmd + ["install", "tensorflow"],
247227
"Installing tensorflow into Python 3.11")
248228

229+
# Adoption complete — safe to read registry
230+
py311 = get_interpreter_after_adopt(venv_root, "3.11")
231+
safe_print(f"[INFO] py311 exe: {py311}")
232+
249233
safe_print("\n[STEP 2] Running modern payload in Python 3.11...")
250234
result = subprocess.run(
251235
[str(py311), __file__, "--run-modern", json.dumps(legacy_data)],
@@ -290,4 +274,4 @@ def multiverse_analysis():
290274
safe_print("[SUCCESS] Context switching, installs, and healing all working!")
291275
else:
292276
safe_print("[FAILED] Check output above.")
293-
safe_print(f"[PERF] Total: {time.perf_counter() - t0:.2f}s")
277+
safe_print(f"[PERF] Total: {time.perf_counter() - t0:.2f}s")

0 commit comments

Comments
 (0)