Skip to content

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

Merged
itamaro merged 1 commit into
python:3.15from
miss-islington:backport-5b55b50-3.15
Oct 22, 2026
Merged

itamaro merged 1 commit into
python:3.15from
miss-islington:backport-5b55b50-3.15

Conversation

@miss-islington

@miss-islington miss-islington commented Oct 22, 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
(cherry picked from commit 5b55b50)

Co-authored-by: Itamar Oren itamarost@gmail.com

… executable has no stdlib (pythonGH-160123)

pythonGH-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 pythonGH-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 pythonGH-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 pythonGH-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>
@miss-islington
miss-islington requested a review from FFY00 as a code owner October 22, 2026 21:35
@itamaro
itamaro enabled auto-merge (squash) October 22, 2026 21:46
@itamaro
itamaro merged commit 522bc3d into python:3.15 Oct 22, 2026
56 checks passed
@miss-islington
miss-islington deleted the backport-5b55b50-3.15 branch October 22, 2026 23:28
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