Skip to content

gh-106045: Fix venv path discovery for symlinked executables - #156483

Merged
ambv merged 2 commits into
python:mainfrom
jjhelmus:getpath_use_base_executable
Oct 21, 2026
Merged

ambv merged 2 commits into
python:mainfrom
jjhelmus:getpath_use_base_executable

Conversation

@jjhelmus

@jjhelmus jjhelmus commented Aug 27, 2026 •

Copy link
Copy Markdown
Contributor

Use the resolved base executable when locating the base installation instead of relying on the home directory in pyvenv.cfg.

Add tests covering relocated Python installations and misleading standard-library landmarks near executable symlinks.

Use the resolved base executable when locating the base installation
instead of relying on the home directory in pyvenv.cfg.

Add tests covering relocated Python installations and misleading
standard-library landmarks near executable symlinks.
@jjhelmus

Copy link
Copy Markdown
Contributor Author

This will likely also address: #127440
Which has another proposed fix: #127441

Comment thread Lib/test/test_getpath.py
ns.add_known_file("/venv/pyvenv.cfg", [
"home = /public/bin",
])
expected = dict(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why the dict() calls when a dict literal would have worked?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Good catch! A dictionary literal {...} is cleaner, more pythonic, and slightly faster than calling dict(). I'll update this line to use a literal instead.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The dict() call matches the pattern established in the existing tests.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

(agree it makes sense to match all the other tests in the file instead)

@ambv ambv added needs backport to 3.14 bugs and security fixes needs backport to 3.15 bugs and security fixes labels Oct 21, 2026
@ambv
ambv merged commit e07817d into python:main Oct 21, 2026
55 checks passed
@miss-islington-app

Copy link
Copy Markdown

Thanks @jjhelmus for the PR, and @ambv for merging it 🌮🎉.. I'm working now to backport this PR to: 3.14, 3.15.
🐍🍒⛏🤖 I'm not a witch! I'm not a witch!

@bedevere-app

bedevere-app Bot commented Oct 21, 2026

Copy link
Copy Markdown

GH-160002 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 21, 2026
@bedevere-app

bedevere-app Bot commented Oct 21, 2026

Copy link
Copy Markdown

GH-160003 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 21, 2026
ambv pushed a commit that referenced this pull request Oct 21, 2026
…H-156483) (#160002)

Use the resolved base executable when locating the base installation
instead of relying on the home directory in pyvenv.cfg.

Add tests covering relocated Python installations and misleading
standard-library landmarks near executable symlinks.

(cherry picked from commit e07817d)

Co-authored-by: Jonathan J. Helmus <jjhelmus@gmail.com>
ambv pushed a commit that referenced this pull request Oct 21, 2026
…H-156483) (GH-160003)

(cherry picked from commit e07817d)

Co-authored-by: Jonathan J. Helmus <jjhelmus@gmail.com>
itamaro added a commit to itamaro/cpython that referenced this pull request Oct 22, 2026
… 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
itamaro added a commit that referenced this pull request Oct 22, 2026
…table has no stdlib (#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>
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.

5 participants