@@ -9272,6 +9272,7 @@ def _fallback_to_download(self, version: str) -> int:
92729272 and includes a safety stop to prevent deleting the active interpreter.
92739273
92749274 CRITICAL FIX: Always uses the REAL venv root, not nested paths.
9275+ CRITICAL FIX 2: Handles version upgrades (e.g., 3.11.9 -> 3.11.14 for musl)
92759276 """
92769277 safe_print(_("\n--- Running robust download strategy ---"))
92779278 try:
@@ -9293,15 +9294,12 @@ def _fallback_to_download(self, version: str) -> int:
92939294 return 1
92949295
92959296 # CRITICAL: Force recalculation of venv_path to ensure we're not using a nested path
9296- # This prevents installing Python 3.13 inside Python 3.14's directory
92979297 real_venv_path = self.config_manager._get_venv_root()
92989298
92999299 # Double-check: if the venv_path contains .omnipkg, something is wrong
93009300 if ".omnipkg" in str(real_venv_path):
93019301 safe_print(_(" - ⚠️ WARNING: Detected nested path in venv_path!"))
93029302 safe_print(_(" - Current venv_path: {}").format(real_venv_path))
9303-
9304- # Extract the real root by taking everything before first .omnipkg
93059303 path_str = str(real_venv_path).replace("\\", "/")
93069304 parts = path_str.split("/.omnipkg/")
93079305 if len(parts) >= 2:
@@ -9316,42 +9314,31 @@ def _fallback_to_download(self, version: str) -> int:
93169314
93179315 from filelock import FileLock
93189316
9319- # Wait up to 5 minutes for other processes to finish installing
93209317 with FileLock(lock_path, timeout=300):
93219318 safe_print(_(" - Target installation path: {}").format(dest_path))
93229319
93239320 # RE-CHECK existence inside the lock!
93249321 if dest_path.exists():
93259322 safe_print(
9326- _(
9327- " - Found existing directory for Python {}. Verifying integrity..."
9328- ).format(full_version)
9323+ _(" - Found existing directory for Python {}. Verifying integrity...").format(full_version)
93299324 )
93309325 if self._is_interpreter_directory_valid(dest_path):
9331- safe_print(
9332- _(" - ✅ Integrity check passed. Installation is valid and complete.")
9333- )
9326+ safe_print(_(" - ✅ Integrity check passed. Installation is valid and complete."))
93349327 return 0
93359328 else:
93369329 safe_print(
9337- _(
9338- " - ⚠️ Integrity check failed: Incomplete installation detected (missing or broken executable)."
9339- )
9330+ _(" - ⚠️ Integrity check failed: Incomplete installation detected (missing or broken executable).")
93409331 )
93419332
93429333 # Safety check: don't delete the currently active interpreter
93439334 try:
93449335 active_interpreter_root = Path(sys.executable).resolve().parents[1]
93459336 if dest_path.resolve() == active_interpreter_root:
93469337 safe_print(
9347- _(
9348- " - ❌ CRITICAL ERROR: The broken interpreter is the currently active one!"
9349- )
9338+ _(" - ❌ CRITICAL ERROR: The broken interpreter is the currently active one!")
93509339 )
93519340 safe_print(
9352- _(
9353- " - Aborting to prevent self-destruction. Please fix the environment manually."
9354- )
9341+ _(" - Aborting to prevent self-destruction. Please fix the environment manually.")
93559342 )
93569343 return 1
93579344 except (IndexError, OSError):
@@ -9363,9 +9350,7 @@ def _fallback_to_download(self, version: str) -> int:
93639350 safe_print(_(" - ✅ Removed broken directory successfully."))
93649351 except Exception as e:
93659352 safe_print(
9366- _(
9367- " - ❌ FATAL: Failed to remove existing broken directory: {}"
9368- ).format(e)
9353+ _(" - ❌ FATAL: Failed to remove existing broken directory: {}").format(e)
93699354 )
93709355 return 1
93719356
@@ -9375,9 +9360,7 @@ def _fallback_to_download(self, version: str) -> int:
93759360 # Try Python 3.13 alternative download method
93769361 if version == "3.13":
93779362 safe_print(_(" - Using python-build-standalone for Python 3.13..."))
9378- download_success = self._download_python_313_alternative(
9379- dest_path, full_version
9380- )
9363+ download_success = self._download_python_313_alternative(dest_path, full_version)
93819364
93829365 # Fallback to other download methods
93839366 if not download_success:
@@ -9390,23 +9373,20 @@ def _fallback_to_download(self, version: str) -> int:
93909373
93919374 # CRITICAL FIX: Detect actual installed directory
93929375 # The installer may upgrade versions (e.g., 3.11.9 -> 3.11.14 for musl)
9393- actual_install_dir = python_exe.parent.parent
9394- if actual_install_dir.exists():
9395- dest_path = actual_install_dir
9396- safe_print(f" - ✅ Detected actual installation at: {dest_path}")
9376+ if python_exe and hasattr(python_exe, 'parent'):
9377+ actual_install_dir = python_exe.parent.parent
9378+ if actual_install_dir.exists() and actual_install_dir != dest_path:
9379+ safe_print(f" - 🔄 Version upgraded: {dest_path.name} -> {actual_install_dir.name}")
9380+ dest_path = actual_install_dir
93979381
93989382 except Exception as e:
9399- safe_print(
9400- _(" - Warning: _install_managed_python failed: {}").format(e)
9401- )
9383+ safe_print(_(" - Warning: _install_managed_python failed: {}").format(e))
94029384 elif hasattr(self.config_manager, "install_managed_python"):
94039385 try:
94049386 self.config_manager.install_managed_python(real_venv_path, full_version)
94059387 download_success = True
94069388 except Exception as e:
9407- safe_print(
9408- _(" - Warning: install_managed_python failed: {}").format(e)
9409- )
9389+ safe_print(_(" - Warning: install_managed_python failed: {}").format(e))
94109390 elif hasattr(self.config_manager, "download_python"):
94119391 try:
94129392 self.config_manager.download_python(full_version)
@@ -9415,29 +9395,40 @@ def _fallback_to_download(self, version: str) -> int:
94159395 safe_print(_(" - Warning: download_python failed: {}").format(e))
94169396
94179397 if not download_success:
9418- safe_print(
9419- _("❌ Error: All download methods failed for Python {}").format(
9420- full_version
9421- )
9422- )
9398+ safe_print(_("❌ Error: All download methods failed for Python {}").format(full_version))
94239399 return 1
94249400
9401+ # FALLBACK: If dest_path doesn't exist, search for upgraded versions
9402+ if not dest_path.exists():
9403+ major_minor = ".".join(full_version.split(".")[:2]) # "3.11"
9404+ interpreters_dir = real_venv_path / ".omnipkg" / "interpreters"
9405+
9406+ safe_print(f" - 🔍 Searching for any Python {major_minor}.* installation...")
9407+ for candidate in sorted(interpreters_dir.glob(f"cpython-{major_minor}.*"), reverse=True):
9408+ if candidate.is_dir():
9409+ safe_print(f" - Found candidate: {candidate}")
9410+ if self._is_interpreter_directory_valid(candidate):
9411+ dest_path = candidate
9412+ safe_print(f" - ✅ Using upgraded installation at: {dest_path}")
9413+ break
9414+
94259415 # Verify installation
94269416 if dest_path.exists() and self._is_interpreter_directory_valid(dest_path):
94279417 safe_print(_(" - ✅ Download and installation completed successfully."))
94289418 self.config_manager._set_rebuild_flag_for_version(version)
94299419 return 0
94309420 else:
9431- safe_print(_(" - ❌ Installation completed but integrity check still fails."))
9421+ safe_print(f" - ❌ Installation completed but integrity check still fails.")
9422+ safe_print(f" - Checked path: {dest_path}")
9423+ safe_print(f" - Path exists: {dest_path.exists()}")
94329424 return 1
94339425
94349426 except Exception as e:
94359427 safe_print(_("❌ Download and installation process failed: {}").format(e))
94369428 import traceback
9437-
94389429 traceback.print_exc()
94399430 return 1
9440-
9431+
94419432 # Add this at the START of _download_python_313_alternative,
94429433 # right after the function signature:
94439434
0 commit comments