Skip to content

gh-106045: Keep searching from venv home when the resolved base executable has no stdlib - #160123

Merged
itamaro merged 5 commits into
python:mainfrom
itamaro:gh-106045-venv-home-fallback
Oct 22, 2026
Merged

itamaro merged 5 commits into
python:mainfrom
itamaro:gh-106045-venv-home-fallback

Conversation

@itamaro

@itamaro itamaro commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

GH-156483 made getpath locate a venv's base installation relative to the resolved base executable instead of the home key in pyvenv.cfg. When the resolved executable lives outside the installation tree (e.g. a launcher binary symlinked into it), nothing is found there and discovery falls back to the compiled-in PREFIX. The venv then either fails to start or silently runs with another installation's standard library.

This PR:

  • Keeps gh-106045: Fix venv path discovery for symlinked executables #156483's behavior when the resolved executable's directory locates a standard library (pybuilddir.txt, the stdlib zip, or os.py). Otherwise it keeps searching from home, as before gh-106045: Fix venv path discovery for symlinked executables #156483.
  • In that fallback case, still searches the resolved executable's directory for lib-dynload when determining exec_prefix: after prefix, before home and the compiled-in EXEC_PREFIX. Layouts with a separate exec prefix (e.g. ./configure --exec-prefix) then keep loading extension modules from the right place, and a stale lib-dynload near the home symlink can't take precedence over the one beside the resolved executable.

The tests added by GH-156483 pass unchanged. Three new test_getpath cases cover the launcher-outside-the-installation and separate-exec-prefix layouts, and a misleading lib-dynload near home; each fails without this change.

Reproducer

On main (0a86a34), with a plain ./configure --prefix=$HOME/py-under-test && make && make install:

#!/bin/sh
# Usage: venv-home-repro.sh <installed-prefix> <X.Y>
set -eu
PREFIX=$1; VER=$2
W=$(mktemp -d)
mkdir -p "$W/runtime/bin" "$W/runtime/lib" "$W/launcher"
cp -R "$PREFIX/lib/python$VER" "$W/runtime/lib/"
cp "$PREFIX/bin/python$VER" "$W/launcher/python3"
ln -s "$W/launcher/python3" "$W/runtime/bin/python3"
PYTHONHOME="$W/runtime" "$W/runtime/bin/python3" -m venv --without-pip "$W/venv"
grep '^home' "$W/venv/pyvenv.cfg"
echo "expected base_prefix: $W/runtime"
"$W/venv/bin/python" -c 'import os, sys; print("actual base_prefix:", sys.base_prefix); print("os.py loaded from:", os.__file__)'

Before:

home = /tmp/tmp.NFug9wEr4h/runtime/bin
expected base_prefix: /tmp/tmp.NFug9wEr4h/runtime
actual base_prefix: /home/user/py-under-test
os.py loaded from: /home/user/py-under-test/lib/python3.16/os.py

(With $HOME/py-under-test/lib/python3.16 removed, the venv instead fails with ModuleNotFoundError: No module named 'encodings'.)

After:

home = /tmp/tmp.8Zne2otFrw/runtime/bin
expected base_prefix: /tmp/tmp.8Zne2otFrw/runtime
actual base_prefix: /tmp/tmp.8Zne2otFrw/runtime
os.py loaded from: /tmp/tmp.8Zne2otFrw/runtime/lib/python3.16/os.py

Written with assistance from Claude Code Fable 5.1

Comment thread Modules/getpath.py
@bedevere-app

bedevere-app Bot commented Oct 21, 2026

Copy link
Copy Markdown

When you're done making the requested changes, leave the comment: I have made the requested changes; please review again.

@ambv

ambv commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

If you rebase, the Hypo failure is fixed.

itamaro and others added 2 commits October 21, 2026 17:28
… executable has no stdlib

pythonGH-156483 searches for a venv's base installation relative to the
resolved base executable. When that executable lives outside the
installation tree that 'home' in pyvenv.cfg points into (e.g. a launcher
binary symlinked into it), nothing is found there and discovery falls
back to the compiled-in PREFIX.

Only prefer the resolved executable's directory when a standard library
landmark is found from there; otherwise keep searching from 'home'.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A93JzpSGH27i6PNX8re4YV
Even when stdlib discovery falls back to 'home', the resolved base
executable may live under a separate exec_prefix (e.g. ./configure
--exec-prefix), so search there for platform dependent libraries before
falling back to the compiled-in EXEC_PREFIX.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A93JzpSGH27i6PNX8re4YV
@itamaro
itamaro force-pushed the gh-106045-venv-home-fallback branch from 620ac1e to 72b32a4 Compare October 22, 2026 00:33
@itamaro

itamaro commented Oct 22, 2026

Copy link
Copy Markdown
Contributor Author

I have made the requested changes; please review again.

@bedevere-app

bedevere-app Bot commented Oct 22, 2026

Copy link
Copy Markdown

Thanks for making the requested changes!

@ambv: please review the changes made to this pull request.

@bedevere-app
bedevere-app Bot requested a review from ambv October 22, 2026 00:42
Comment thread Modules/getpath.py Outdated
Comment thread Lib/test/test_getpath.py
itamaro and others added 2 commits October 22, 2026 11:08
Co-authored-by: Łukasz Langa <lukasz@langa.pl>
@ambv ambv removed the skip news label Oct 22, 2026
@ambv

ambv commented Oct 22, 2026

Copy link
Copy Markdown
Contributor

We can no longer "skip news" for this if it shipped to 3.14.9 and 3.15.1.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A93JzpSGH27i6PNX8re4YV
@ambv

ambv commented Oct 22, 2026

Copy link
Copy Markdown
Contributor

@itamaro nitpick: update the description before landing to match the code.

It says the resolved executable's directory is searched for lib-dynload "after prefix and home". Since your reorder, it's searched after prefix but before home (Modules/getpath.py:636-642). And there's three cases.

@itamaro

itamaro commented Oct 22, 2026

Copy link
Copy Markdown
Contributor Author

@itamaro nitpick: update the description before landing to match the code.

It says the resolved executable's directory is searched for lib-dynload "after prefix and home". Since your reorder, it's searched after prefix but before home (Modules/getpath.py:636-642). And there's three cases.

thanks, updated the description!

@itamaro
itamaro enabled auto-merge (squash) October 22, 2026 20:26
@itamaro
itamaro merged commit 5b55b50 into python:main Oct 22, 2026
62 of 63 checks passed
@miss-islington-app

Copy link
Copy Markdown

Thanks @itamaro for the PR 🌮🎉.. I'm working now to backport this PR to: 3.14, 3.15.
🐍🍒⛏🤖

@bedevere-app

bedevere-app Bot commented Oct 22, 2026

Copy link
Copy Markdown

GH-160354 is a backport of this pull request to the 3.15 branch.

@bedevere-app bedevere-app Bot removed the needs backport to 3.15 bugs and security fixes label Oct 22, 2026
@bedevere-app

bedevere-app Bot commented Oct 22, 2026

Copy link
Copy Markdown

GH-160355 is a backport of this pull request to the 3.14 branch.

@bedevere-app bedevere-app Bot removed the needs backport to 3.14 bugs and security fixes label Oct 22, 2026
itamaro added a commit that referenced this pull request Oct 22, 2026
…e executable has no stdlib (GH-160123) (#160355)

gh-106045: Keep searching from venv home when the resolved base executable has no stdlib (GH-160123)

GH-156483 made `getpath` locate a venv's base installation relative to the *resolved* base executable instead of the `home` key in `pyvenv.cfg`. When the resolved executable lives outside the installation tree (e.g. a launcher binary symlinked into it), nothing is found there and discovery falls back to the compiled-in `PREFIX`. The venv then either fails to start or silently runs with another installation's standard library.

This PR:

- Keeps GH-156483's behavior when the resolved executable's directory locates a standard library (`pybuilddir.txt`, the stdlib zip, or `os.py`). Otherwise it keeps searching from `home`, as before GH-156483.
- In that fallback case, still searches the resolved executable's directory for `lib-dynload` when determining `exec_prefix`: after `prefix`, before `home` and the compiled-in `EXEC_PREFIX`. Layouts with a separate exec prefix (e.g. `./configure --exec-prefix`) then keep loading extension modules from the right place, and a stale `lib-dynload` near the `home` symlink can't take precedence over the one beside the resolved executable.

The tests added by GH-156483 pass unchanged. Three new `test_getpath` cases cover the launcher-outside-the-installation and separate-exec-prefix layouts, and a misleading `lib-dynload` near `home`; each fails without this change.

<details>
<summary>Reproducer</summary>

On `main` (0a86a34), with a plain `./configure --prefix=$HOME/py-under-test && make && make install`:

```sh
#!/bin/sh
# Usage: venv-home-repro.sh <installed-prefix> <X.Y>
set -eu
PREFIX=$1; VER=$2
W=$(mktemp -d)
mkdir -p "$W/runtime/bin" "$W/runtime/lib" "$W/launcher"
cp -R "$PREFIX/lib/python$VER" "$W/runtime/lib/"
cp "$PREFIX/bin/python$VER" "$W/launcher/python3"
ln -s "$W/launcher/python3" "$W/runtime/bin/python3"
PYTHONHOME="$W/runtime" "$W/runtime/bin/python3" -m venv --without-pip "$W/venv"
grep '^home' "$W/venv/pyvenv.cfg"
echo "expected base_prefix: $W/runtime"
"$W/venv/bin/python" -c 'import os, sys; print("actual base_prefix:", sys.base_prefix); print("os.py loaded from:", os.__file__)'
```

Before:

```
home = /tmp/tmp.NFug9wEr4h/runtime/bin
expected base_prefix: /tmp/tmp.NFug9wEr4h/runtime
actual base_prefix: /home/user/py-under-test
os.py loaded from: /home/user/py-under-test/lib/python3.16/os.py
```

(With `$HOME/py-under-test/lib/python3.16` removed, the venv instead fails with `ModuleNotFoundError: No module named 'encodings'`.)

After:

```
home = /tmp/tmp.8Zne2otFrw/runtime/bin
expected base_prefix: /tmp/tmp.8Zne2otFrw/runtime
actual base_prefix: /tmp/tmp.8Zne2otFrw/runtime
os.py loaded from: /tmp/tmp.8Zne2otFrw/runtime/lib/python3.16/os.py
```

</details>
(cherry picked from commit 5b55b50)

Co-authored-by: Itamar Oren <itamarost@gmail.com>
itamaro added a commit that referenced this pull request Oct 22, 2026
…e executable has no stdlib (GH-160123) (#160354)

gh-106045: Keep searching from venv home when the resolved base executable has no stdlib (GH-160123)

GH-156483 made `getpath` locate a venv's base installation relative to the *resolved* base executable instead of the `home` key in `pyvenv.cfg`. When the resolved executable lives outside the installation tree (e.g. a launcher binary symlinked into it), nothing is found there and discovery falls back to the compiled-in `PREFIX`. The venv then either fails to start or silently runs with another installation's standard library.

This PR:

- Keeps GH-156483's behavior when the resolved executable's directory locates a standard library (`pybuilddir.txt`, the stdlib zip, or `os.py`). Otherwise it keeps searching from `home`, as before GH-156483.
- In that fallback case, still searches the resolved executable's directory for `lib-dynload` when determining `exec_prefix`: after `prefix`, before `home` and the compiled-in `EXEC_PREFIX`. Layouts with a separate exec prefix (e.g. `./configure --exec-prefix`) then keep loading extension modules from the right place, and a stale `lib-dynload` near the `home` symlink can't take precedence over the one beside the resolved executable.

The tests added by GH-156483 pass unchanged. Three new `test_getpath` cases cover the launcher-outside-the-installation and separate-exec-prefix layouts, and a misleading `lib-dynload` near `home`; each fails without this change.

<details>
<summary>Reproducer</summary>

On `main` (0a86a34), with a plain `./configure --prefix=$HOME/py-under-test && make && make install`:

```sh
#!/bin/sh
# Usage: venv-home-repro.sh <installed-prefix> <X.Y>
set -eu
PREFIX=$1; VER=$2
W=$(mktemp -d)
mkdir -p "$W/runtime/bin" "$W/runtime/lib" "$W/launcher"
cp -R "$PREFIX/lib/python$VER" "$W/runtime/lib/"
cp "$PREFIX/bin/python$VER" "$W/launcher/python3"
ln -s "$W/launcher/python3" "$W/runtime/bin/python3"
PYTHONHOME="$W/runtime" "$W/runtime/bin/python3" -m venv --without-pip "$W/venv"
grep '^home' "$W/venv/pyvenv.cfg"
echo "expected base_prefix: $W/runtime"
"$W/venv/bin/python" -c 'import os, sys; print("actual base_prefix:", sys.base_prefix); print("os.py loaded from:", os.__file__)'
```

Before:

```
home = /tmp/tmp.NFug9wEr4h/runtime/bin
expected base_prefix: /tmp/tmp.NFug9wEr4h/runtime
actual base_prefix: /home/user/py-under-test
os.py loaded from: /home/user/py-under-test/lib/python3.16/os.py
```

(With `$HOME/py-under-test/lib/python3.16` removed, the venv instead fails with `ModuleNotFoundError: No module named 'encodings'`.)

After:

```
home = /tmp/tmp.8Zne2otFrw/runtime/bin
expected base_prefix: /tmp/tmp.8Zne2otFrw/runtime
actual base_prefix: /tmp/tmp.8Zne2otFrw/runtime
os.py loaded from: /tmp/tmp.8Zne2otFrw/runtime/lib/python3.16/os.py
```

</details>
(cherry picked from commit 5b55b50)

Co-authored-by: Itamar Oren <itamarost@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3.14 bugs and security fixes 3.15 bugs and security fixes release-blocker

Projects

Development

Successfully merging this pull request may close these issues.

2 participants