Skip to content

Windows: validate managed Python before package installation - #7763

Merged
danielhanchen merged 6 commits into
unslothai:mainfrom
Etherll:pyenv-fix
Aug 3, 2026
Merged

Windows: validate managed Python before package installation#7763
danielhanchen merged 6 commits into
unslothai:mainfrom
Etherll:pyenv-fix

Conversation

@Etherll

@Etherll Etherll commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

Resolve Python PATH shims to their underlying CPython executable and verify that Studio's managed virtual-environment interpreter can launch before GPU detection or package installation.

Broken environments now fail with the managed and base Python paths instead of surfacing later as a misleading PyTorch installation error.

Motivation

A Windows virtual environment can retain Scripts\python.exe after its base Python installation has been removed, renamed, or damaged. The installer previously checked only whether the managed executable existed, then failed later when uv pip tried to inspect it.

Healthy pyenv-win shims work with uv, so the installer should continue supporting them while detecting unusable environments earlier.

Changes

  • Resolve python and python3 PATH launchers through sys.executable and require the resolved target to be a non-Conda executable in install.ps1:1335.
  • Add Test-VenvPythonReady to check that the managed interpreter exists and launches successfully in install.ps1:1684.
  • Run the readiness check before GPU detection or any uv pip command, with diagnostics for both interpreter paths, in install.ps1:1909.
  • Cover .bat shim resolution, missing and non-launchable interpreters, valid interpreters, and validation ordering in tests/python/test_windows_python_venv_hardening.py:43.

How to test

Run on Windows with PowerShell 7 and Windows PowerShell 5.1 available:

python -m pytest tests/python/test_windows_python_venv_hardening.py tests/python/test_windows_arm64_python_choice.py -q -p no:cacheprovider

@Etherll
Etherll requested a review from danielhanchen as a code owner August 2, 2026 20:27
@Etherll

Etherll commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eefa26940e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread install.ps1
substep "$VenvDir"
}

if (-not (Test-VenvPythonReady -PythonExe $VenvPython)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Mark failed managed venvs as installer-owned

When a first install using UNSLOTH_STUDIO_HOME successfully creates Scripts\python.exe but this readiness probe fails, the early exit occurs before .unsloth-studio-owned is written. On the advised rerun, the env-mode guard at install.ps1:1836-1844 sees the executable without any ownership sentinel and refuses to replace it as a potentially unrelated venv, so restoring Python and rerunning cannot recover without manually deleting or moving the directory. Write the ownership marker immediately after successful uv venv creation, or remove the failed installer-created venv before returning.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c1c0b5e: the ownership marker is now written before readiness validation, so an installer-created broken venv remains replaceable on rerun.

Comment thread install.ps1 Outdated
Comment on lines +1915 to +1916
Write-Host " Selected base Python: $($DetectedPython.Path)" -ForegroundColor Yellow
Write-Host " Restore or reinstall the selected base Python, then re-run install.ps1." -ForegroundColor Yellow

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Report the migrated venv's actual base interpreter

When the CWD-relative environment is migrated at install.ps1:1892-1896, it retains the base recorded in its own pyvenv.cfg; $DetectedPython is only the independently selected interpreter found earlier and may be a different, healthy installation. If the migrated interpreter fails this probe because its original base was removed, these lines instruct the user to reinstall an unrelated interpreter, which does not repair that venv. Either identify the base from the migrated environment or advise recreating/rerunning without claiming that $DetectedPython.Path is the base that needs restoration.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c1c0b5e: failure diagnostics now read the recorded home from pyvenv.cfg instead of labeling the newly detected interpreter as the migrated venv base. Python documents this field at https://docs.python.org/3/library/venv.html#creating-virtual-environments

Comment thread install.ps1 Outdated
Comment on lines +1342 to +1343
$resolvedExe = (& $cmd.Source -c "import sys; print(sys.executable)" 2>$null | Out-String).Trim()
if ($resolvedExe -and (Test-Path -LiteralPath $resolvedExe -PathType Leaf) -and -not (Test-IsCondaPython $resolvedExe)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Isolate the executable-path probe from startup output

When a supported PATH Python has a .pth hook or sitecustomize.py that writes a startup banner to stdout, --version still matches but this -c invocation returns the banner and executable path together. Out-String.Trim() therefore produces a multi-line value that cannot pass Test-Path, so the newly added resolution discards a working interpreter and may repeatedly reinstall Python only to reject it again. Run this probe without site initialization (for example with -S) or extract a uniquely marked path rather than treating all stdout as the filename.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c1c0b5e: both executable-path probes now use -S, and the regression fixture emits a startup banner. Python documents -S at https://docs.python.org/3/using/cmdline.html#cmdoption-S

@Etherll

Etherll commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

1 similar comment
@Etherll

Etherll commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

Reviewed commit: 597aeebbca

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@Etherll

Etherll commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

Reviewed commit: 80cdb73ab4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

…nslothai#7763

Get-PythonPlatformTag still probed without -S. Its result is compared with
-eq "win-amd64", so a sitecustomize banner reads as "unknown", the
x64-over-ARM64 preference is lost and Windows on ARM settles for a native
ARM64 interpreter. Test-IsCondaPython gets -S for the same reason. Neither
query needs site, and base_prefix and get_platform() are unchanged by -S on
3.11, 3.12 and 3.13.

The new test module parametrizes over pwsh and powershell, but
cross-platform-parity-ci.yml is the only three-OS job and its paths filter
and pytest list are hardcoded, so the 5.1 leg never ran. Added the file to
both.

Also match the py launcher branch to the PATH branch with -LiteralPath
-PathType Leaf, and fix the failure message: the empty base home leaked into
the Exit-InstallFailure text, and the ownership marker is written before the
gate, so a plain re-run already replaces the environment.
@danielhanchen

Copy link
Copy Markdown
Member

Pushed a follow-up commit to this branch. The change itself is right, it just stops one probe short.

Get-PythonPlatformTag needed the same -S. Its result is compared with -eq "win-amd64", so a sitecustomize that prints to stdout makes the tag read as unknown, the x64-over-ARM64 ranking at the bottom of Find-CompatiblePython then finds no x64 candidate, and Windows on ARM settles for the native ARM64 build. That is exactly what tests/python/test_windows_arm64_python_choice.py exists to prevent. Measured against a real interpreter and a real sitecustomize:

without -S    clean: linux-x86_64    with sitecustomize: "startup_banner" then "linux-x86_64"
with -S       clean: linux-x86_64    with sitecustomize: "linux-x86_64"

End to end on a simulated ARM64 host with an x64 interpreter carrying a site hook, the branch as submitted returned Arch=unknown and started a needless x64 bootstrap, and -X64Only returned $null so the ARM64 recovery path found nothing. Both are correct now. Test-IsCondaPython gets -S for the same reason. Neither query needs site, and sys.base_prefix and sysconfig.get_platform() are identical with and without -S on 3.11, 3.12 and 3.13.

The Windows PowerShell 5.1 leg of the new tests was never running. The module parametrizes over pwsh and powershell, but cross-platform-parity-ci.yml is the only three OS job and both its paths filter and its pytest list are hardcoded, so the file was not picked up there. studio-backend-ci.yml does collect tests/python/, but only on ubuntu-latest where powershell does not exist. Added the file to both lists.

Smaller items in the same commit:

  • the py launcher branch now uses -LiteralPath -PathType Leaf, matching the PATH branch
  • the empty base home no longer leaks into the Exit-InstallFailure text
  • the failure message no longer asks the user to move $VenvDir aside. The ownership marker is written before the gate, so a plain re-run already replaces the environment
  • added test_arch_probe_ignores_startup_output so the arch probe stays banner proof

Everything else held up. I replayed the venv lifecycle against both main and this branch over 28 cells (fresh, current layout, legacy .venv, CWD relative ~/unsloth_studio, owned and unowned, healthy and broken base, default and env mode). Only the broken venv cells differ, they are the ones this PR targets, and every one of them recovers on the next run. GPU detection, torch index selection and package install are byte identical to main, and a 40 cell hardware sweep (CUDA 13.0 down to 11.8, the CUDA UMD Version spelling, unparseable output, nonzero exit, a hung nvidia-smi, and AMD or CPU with no nvidia-smi) picks the same index URL on both. install.sh is untouched, so Linux, WSL and macOS are unaffected.

Thanks for the fix, the underlying report is real. uv hits the same thing in astral-sh/uv#11508 with sitecustomize on stdout, and Comfy-Org/desktop#1574 is the identical broken venv with exit code 103 and No Python at '...'.

One thing I deliberately left out of this PR: neither new probe has a timeout, so a wedged interpreter can hang the installer. Test-IsCondaPython on main hangs the same way, so it is not a regression here, and wiring it into Invoke-NvidiaSmiBounded means moving that helper above the gate. Worth its own PR.

@danielhanchen

Copy link
Copy Markdown
Member

@codex review

@danielhanchen

Copy link
Copy Markdown
Member

pre-commit.ci run

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

Reviewed commit: 79f69a2a36

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@danielhanchen
danielhanchen merged commit 1770182 into unslothai:main Aug 3, 2026
60 of 61 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants