Skip to content

trampolines: Don't set PYTHONHOME, set __PYVENV_LAUNCHER__ only in venvs#19199

Merged
zanieb merged 1 commit intomainfrom
geofft/no-pythonhome-in-trampoline
Apr 29, 2026
Merged

trampolines: Don't set PYTHONHOME, set __PYVENV_LAUNCHER__ only in venvs#19199
zanieb merged 1 commit intomainfrom
geofft/no-pythonhome-in-trampoline

Conversation

@geofft
Copy link
Copy Markdown
Contributor

@geofft geofft commented Apr 27, 2026

PYTHONHOME isn't automatically cleaned up, so setting it causes it to leak to child processes of Python, e.g., other, unrelated (perhaps not even uv-controlled) Python builds, which breaks them. Never setting it fixes #19080.

However, on the implementation of getpath on Python 3.10 and below, leaving PYTHONHOME unset and pointing __PYVENV_LAUNCHER__ at something that isn't a venv or a directly-usable Python installation (for example, pointing it to .local\bin\python3.10.exe) causes Python startup to fail. So we only should be setting it inside a venv, i.e., if the trampoline itself (not the target executable) is in a venv.

Presumably this failure on 3.10 is why we started setting PYTHONHOME in the first place. See brief discussion in
https://github.com/astral-sh/uv/pull/13531/files#diff-969979506be03e89476feade2edebb4689a9c261f325988d3c7efc5e51de26d1

The putative benefit of setting __PYVENV_LAUNCHER__ is to force Python's idea of its own path to be the non-resolved junction path (e.g. uv\python\cpython-3.10-..., not uv\python\cpython-3.10.0-...), to help transparent version upgrades. If we don't manually set it to the non-resolved path, there's a risk that Windows will resolve it. However, empirically on Server 2022, Windows does not resolve it. (Also, even if it does get canonicalized, that has a very limited impact on transparent version upgrades, as venvs still list the junction path in pyvenv.cfg and inside the trampoline. The only impact here is that sys.path etc. might end up listing patch-version-specific paths, which would impact some cases where code serializes the full path to something in the stdlib or to sys._base_executable and calls it later. Also, Linux builds of python-build-standalone have been fully canonicalizing this path since December when we patched getpath to look at /proc/self/exe. That new behavior is arguably a bug but this hasn't seemed to cause any practical issues, so it doesn't seem like a real problem to risk Windows perhaps doing the same thing.)

Unrelatedly, while we're rebuilding trampolines anyway, link with /DEBUG:NONE to avoid storing the debug directory in the PE file, which should slightly (but not entirely) reduce the amount of binary churn.

Test Plan

Will need to be manually tested, I don't think we have good coverage for this.

PYTHONHOME isn't automatically cleaned up, so setting it causes it to
leak to child processes of Python, e.g., other, unrelated (perhaps not
even uv-controlled) Python builds, which breaks them. Never setting it
fixes #19080.

However, on the implementation of getpath on Python 3.10 and below,
leaving PYTHONHOME unset and pointing __PYVENV_LAUNCHER__ at something
that isn't a venv or a directly-usable Python installation (for example,
pointing it to .local\bin\python3.10.exe) causes Python startup to fail.
So we only should be setting it inside a venv, i.e., if the trampoline
itself (not the target executable) is in a venv.

Presumably this failure on 3.10 is why we started setting PYTHONHOME in
the first place. See brief discussion in
https://github.com/astral-sh/uv/pull/13531/files#diff-969979506be03e89476feade2edebb4689a9c261f325988d3c7efc5e51de26d1

The putative benefit of setting __PYVENV_LAUNCHER__ is to force Python's
idea of its own path to be the non-resolved junction path (e.g.
uv\python\cpython-3.10-..., not uv\python\cpython-3.10.0-...), to help
transparent version upgrades. If we don't manually set it to the
non-resolved path, there's a risk that Windows will resolve it. However,
empirically on Server 2022, Windows does not resolve it. (Also, even if
it does get canonicalized, that has a very limited impact on transparent
version upgrades, as venvs still list the junction path in pyvenv.cfg
and inside the trampoline. The only impact here is that sys.path etc.
might end up listing patch-version-specific paths, which would impact
some cases where code serializes the full path to something in the
stdlib or to sys._base_executable and calls it later. Also, Linux builds
of python-build-standalone have been fully canonicalizing this path
since December when we patched getpath to look at /proc/self/exe. That
new behavior is arguably a bug but this hasn't seemed to cause any
practical issues, so it doesn't seem like a real problem to risk Windows
perhaps doing the same thing.)

Unrelatedly, while we're rebuilding trampolines anyway, link with
/DEBUG:NONE to avoid storing the debug directory in the PE file, which
should slightly (but not entirely) reduce the amount of binary churn.
@geofft geofft requested review from konstin and zanieb April 27, 2026 23:05
@geofft geofft added the windows Specific to the Windows platform label Apr 27, 2026
@geofft
Copy link
Copy Markdown
Contributor Author

geofft commented Apr 27, 2026

I've done some manual testing by binary-editing the trampoline EXEs on a Windows system (Server 2022 running on AWS, one valid piece of feedback would be to test on more versions of Windows) to set XYTHONHOME and __XYVENV_LAUNCHER__ instead of the correctly-spelled environment variables. I can do some more testing once we have a binary from CI with the updated trampolines.

One noticeable side effect is that for .local\bin\python3.x.exe, sys.executable changes from pointing to the trampoline to pointing to the junction path (e.g., C:\Users\Administrator\AppData\Roaming\uv\python\cpython-3.10-windows-x86_64-none\python.exe). There's also a sys.path entry for directory of the Python executable, for some reason, and that changes too. I think this change is harmless (sys.executable is still pointing to the junction path so transparent upgrades work, and the sys.path entry is weird and almost certainly erroneous to use). Other than that, I don't think there are any user-visible changes in behavior, besides of course not leaking PYTHONHOME.

Also FYI I built the trampolines on an untrusted dev host, so I'm really hoping that we have the CI check that they were untampered. Reviewers, please double-check that you see that!

I used Claude Sonnet 4.7 extensively to validate my analysis of the code (notably to point out the issue with Python 3.10) and review the change, but I made the (tiny) code change myself.

@zanieb zanieb merged commit 4879934 into main Apr 29, 2026
62 checks passed
@zanieb zanieb deleted the geofft/no-pythonhome-in-trampoline branch April 29, 2026 17:24
@zanieb zanieb added the great writeup A wonderful example of a quality contribution 💜 label Apr 29, 2026
tmeijn pushed a commit to tmeijn/dotfiles that referenced this pull request May 8, 2026
This MR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [uv](https://github.com/astral-sh/uv) | patch | `0.11.7` → `0.11.11` |

MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot).

**Proposed changes to behavior should be submitted there as MRs.**

---

### Release Notes

<details>
<summary>astral-sh/uv (uv)</summary>

### [`v0.11.11`](https://github.com/astral-sh/uv/blob/HEAD/CHANGELOG.md#01111)

[Compare Source](astral-sh/uv@0.11.10...0.11.11)

Released on 2026-05-06.

##### Bug fixes

- Accept legacy ID format from pre-0.11.9 cache entries ([#&#8203;19301](astral-sh/uv#19301))

### [`v0.11.10`](https://github.com/astral-sh/uv/blob/HEAD/CHANGELOG.md#01110)

[Compare Source](astral-sh/uv@0.11.9...0.11.10)

Released on 2026-05-05.

##### Bug fixes

- Allow pre-release Python requests with non-zero patch versions ([#&#8203;19286](astral-sh/uv#19286))

### [`v0.11.9`](https://github.com/astral-sh/uv/blob/HEAD/CHANGELOG.md#0119)

[Compare Source](astral-sh/uv@0.11.8...0.11.9)

Released on 2026-05-04.

This release includes a special release candidate for the next Python 3.14 patch release. Python 3.14 included a new garbage collection implementation, which reduced pause times but caused significant unexpected memory pressure in production environments. In 3.14.5 and 3.15, the previous garbage collection implementation will be restored.

We would greatly appreciate if you tested the 3.14.5rc1 version included in this release. The stable version is expected to be released soon and any feedback on potential issues would be helpful to the Python development team.

For more context, see the [announcement](https://discuss.python.org/t/reverting-the-incremental-gc-in-python-3-14-and-3-15/107014), [issue](python/cpython#148726), and [pull request](python/cpython#148720).

Issues with the new release can be reported in the uv or CPython issue trackers.

##### Python

- Upgrade PyPy to v7.3.22
- Add CPython 3.14.5rc1
- On macOS, CPython statically links `libpython` to match Linux

##### Enhancements

- Omit compatible release desugaring for pre-release hints ([#&#8203;19267](astral-sh/uv#19267))
- Fix file locks on Android ([#&#8203;18323](astral-sh/uv#18323))

##### Preview

- `uv audit` add reporting for adverse project statuses ([#&#8203;19128](astral-sh/uv#19128))

##### Bug fixes

- Discover versioned Python executables when `requires-python` pins a version ([#&#8203;18700](astral-sh/uv#18700))
- Fix URL prefix matching to require path boundaries ([#&#8203;19154](astral-sh/uv#19154))
- Fix transitive Git path dependencies in lockfiles ([#&#8203;19269](astral-sh/uv#19269))
- Handle incorrect unlock error in `LockedFile::drop` on Wine ([#&#8203;19229](astral-sh/uv#19229))
- Prevent uninstalling site-packages for empty `top_level.txt` in `.egg-info` ([#&#8203;19114](astral-sh/uv#19114))
- Use symlinks instead of junctions on Wine ([#&#8203;19213](astral-sh/uv#19213))
- Fix floating-point environment handling on ARMv7 ([#&#8203;19157](astral-sh/uv#19157))
- Redact credentials from remote requirements URL in offline errors ([#&#8203;19216](astral-sh/uv#19216))
- Windows tramplolines no longer set `PYTHONHOME` and only set `__PYVENV_LAUNCHER__` for virtual environments ([#&#8203;19199](astral-sh/uv#19199))

##### Documentation

- Mark `--native-tls` and `UV_NATIVE_TLS` as deprecated ([#&#8203;18705](astral-sh/uv#18705))
- Re-add `pytorch-triton-rocm` to PyTorch ROCm docs ([#&#8203;19241](astral-sh/uv#19241))
- Tweak changelog entries for 0.11.8 ([#&#8203;19188](astral-sh/uv#19188))
- Add 'Exporting lockfiles' to the Concepts->Projects index ([#&#8203;19209](astral-sh/uv#19209))
- Clarify that `uv init` creates git files / folders in the projects guide ([#&#8203;19183](astral-sh/uv#19183))

### [`v0.11.8`](https://github.com/astral-sh/uv/blob/HEAD/CHANGELOG.md#0118)

[Compare Source](astral-sh/uv@0.11.7...0.11.8)

Released on 2026-04-27.

##### Enhancements

- Add `--python-downloads-json-url` to `python pin` ([#&#8203;19092](astral-sh/uv#19092))
- Fetch uv from Astral mirror during self-update ([#&#8203;18682](astral-sh/uv#18682))
- Support `pip uninstall -y` ([#&#8203;19082](astral-sh/uv#19082))
- Allow `exclude-newer` to be missing from the lockfile when `exclude-newer-span` is present ([#&#8203;19024](astral-sh/uv#19024))
- Only show the version number in `uv self version --short` ([#&#8203;19019](astral-sh/uv#19019))
- Silence warnings on empty `SSL_CERT_DIR` directory ([#&#8203;19018](astral-sh/uv#19018))
- Use a sentinel timestamp for relative `exclude-newer` and `exclude-newer-package` values in lockfiles ([#&#8203;19022](astral-sh/uv#19022), [#&#8203;19101](astral-sh/uv#19101))

##### Configuration

- Add `UV_PYTHON_NO_REGISTRY` ([#&#8203;19035](astral-sh/uv#19035))
- Add an environment variable for `UV_NO_PROJECT` ([#&#8203;19052](astral-sh/uv#19052))
- Expose `UV_PYTHON_SEARCH_PATH` for Python discovery `PATH` overrides ([#&#8203;19034](astral-sh/uv#19034))

##### Bug fixes

- Add `rust-toolchain.toml` to uv-build sdist ([#&#8203;19131](astral-sh/uv#19131))
- Ensure uv invocations of git do not inherit repository location environment variables ([#&#8203;19088](astral-sh/uv#19088))
- Redact pre-signed upload URLs in verbose output ([#&#8203;19146](astral-sh/uv#19146))
- Handle transitive URL dependencies in PEP 517 build requirements ([#&#8203;19076](astral-sh/uv#19076), [#&#8203;19086](astral-sh/uv#19086))
- Support `uv lock` on a `pyproject.toml` that only contains dependency-groups ([#&#8203;19087](astral-sh/uv#19087))
- Disable transparent Python upgrades in projects when a patch version is requested via `.python-version` ([#&#8203;19102](astral-sh/uv#19102))
- Fix Python variant tagging in the Windows registry ([#&#8203;19012](astral-sh/uv#19012))
- Ban external symlinks in `.tar.zst` wheels ([#&#8203;19144](astral-sh/uv#19144))

##### Distributions

- Remove deprecated license classifiers from uv-build and add Python 3.14 classifier ([#&#8203;19130](astral-sh/uv#19130))

##### Documentation

- Bump astral-sh/setup-uv version in docs ([#&#8203;19030](astral-sh/uv#19030))
- Update PyTorch documentation for PyTorch 2.11 ([#&#8203;19095](astral-sh/uv#19095))

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this MR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNjguNSIsInVwZGF0ZWRJblZlciI6IjQzLjE2OC41IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJSZW5vdmF0ZSBCb3QiLCJhdXRvbWF0aW9uOmJvdC1hdXRob3JlZCIsImRlcGVuZGVuY3ktdHlwZTo6cGF0Y2giXX0=-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

great writeup A wonderful example of a quality contribution 💜 windows Specific to the Windows platform

Projects

None yet

Development

Successfully merging this pull request may close these issues.

uv 0.11.7 with 3.13 interpreter on windows fails to build python wheel for cp311/cp312 using cibuildwheel

2 participants