You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
venv base path does not resolve symlinks using realpath() #106045
It seems there is a bug in venv (and a similar one in virtualenv) where the "base" path in pyvenv.cfg is set incorrectly. If Python is installed in a non-standard folder, e.g. /usr/local/python-X.Y.Z and then symlinked into /usr/local/bin/python3, the venv package does not work correctly. I believe the source of the trouble is the setting of "home" variable. Specifically, this line:
The dirname result is used to set "home". If the executable path is /usr/local/bin/python3 (actually a symlink to /usr/local/python-X.Y.Z/bin/python3), the "home" should not be set to /usr/local. Changing the above line (in the ensure_directories()) function to:
This fixes the problem. I believe this is consistent with what the getpath.py module in Python does.
I noticed with problem when running the most recent Debian OS, version 12. It includes Python 3.11 and therefore /usr/lib/python3.11 exists. With the above bug, the venv tries to use /usr/lib/python3.11 as the sys.path. Importing the struct module fails with the mysterious error:
>>> import struct
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.11/struct.py", line 13, in <module>
from _struct import *
ModuleNotFoundError: No module named '_struct'
I've run into this as well:
Python built from source & installed in /opt/python-3.11, symlink in /usr/local/bin/python3.11.
In my case, it was on ubuntu 22.04, the venv could not be created with a failure to import subprocess because indeed it was trying to use /usr/lib/python3.11 for lib-dynload in sys.path.
Changing home manually or patching the venv module worked.
I noticed with problem when running the most recent Debian OS, version 12. It includes Python 3.11 and therefore /usr/lib/python3.11 exists. With the above bug, the venv tries to use /usr/lib/python3.11 as the sys.path. Importing the struct module fails with the mysterious error:
Do you have instructions how to reproduce this? We'd like to avoid this problem in uv.
Then venv/bin/python -c "import struct; import subprocess" works fine for me.
Python built from source & installed in /opt/python-3.11, symlink in /usr/local/bin/python3.11.
Same questions, do you have instructions how to reproduce this (outside the github actions one)? I tried this with a python checkout (4d3ee77):
./configure --prefix=/opt/cpython1 --enable-optimizations
make -s -j32
sudo make install
Then i tried:
mkdir a
cd a
ln -s /opt/cpython1/bin/python3 python3
cd ..
mkdir b
cd b
../a/python3 -m venv venv
venv/bin/python -c "import struct; import subprocess"cd ..
mkdir c
cd c
virtualenv -p ../a/python3 venv
venv/bin/python -c "import struct; import subprocess"
I could reproduce it using your dockerfile with a slight modification.
The folder /usr/lib/python3.11/lib-dynload should exist for the venv creation to fail.
You can mkdir the folder or also add python3-gdbm to the list of packages to install.
To summarize, the error only happens (editing this to 3.13 to be actionable on main) when you have a custom built python that is linked to exactly /usr/local/bin/python3, but there's also a system python of the same version installed in /usr installed that created /usr/lib/python3.13/lib-dynload?
I've tried to trace the cause and it seems that installed_platbase gets switched to /usr on venv creation:
I haven't looked into this in detail, but might the real issue here be that you're effectively running the interpreter in a broken system-wide environment under /usr/local? In other words, don't just symlink the interpreter. (Disclaimer: I'm not an expert in the sysconfig machinery either.)
The actual command used to launch the interpreter determines what's considered the environment, even if it's a symbolic link into a valid installation. To my knowledge, sysconfig uses the nominal (unresolved) interpreter path to expand the location templates in installation schemes. (not sure about that, should double check)
I haven't looked into this in detail, but might the real issue here be that you're effectively running the interpreter in a broken system-wide environment under /usr/local?
I don't know what a "broken system-wide environment" means. As I said in the description, I think this is a bug in venv since it doesn't match what getpath.c does.
Rather than having venv have complicated logic to figure out what "home" should be, perhaps it should just run something like: python3 -c "import sysconfig; print(sysconfig.get_paths())".
I've worked around the issue by creating wrapper shell scripts for executables, rather than using symlinks, e.g. usr/local/bin/python3 contains:
on POSIX builds with a shared libpython, this should now be mitigated by looking at the libpython directory before resorting to the base interpreter, which should be much more reliable.
It does not seem to be helping in this case (on macOS):
diff --git a/Modules/getpath.py b/Modules/getpath.py
index be2210345a..6607aacb80 100644
--- a/Modules/getpath.py+++ b/Modules/getpath.py@@ -412,6 +412,9 @@ def search_up(prefix, *landmarks, test=isfile):
if isfile(candidate):
base_executable = candidate
break
+ if base_executable and isfile(base_executable):+ # Update the executable directory to be based on the resolved base executable+ executable_dir = real_executable_dir = dirname(base_executable)
break
else:
# We didn't find a 'home' key in pyvenv.cfg (no break), reset venv_prefix.
I frequently encounter this problem because of how I've set up my multiple local builds of Python (used to test version compatibility for packages, and also to investigate differences between Python versions). Because of the wrong home path, the standard library can't be found at all, with more dramatic consequences:
$ py3.13 -m venv --without-pip test && test/bin/python
Could not find platform independent libraries <prefix>
Could not find platform dependent libraries <exec_prefix>
Fatal Python error: Failed to import encodings module
Python runtime state: core initialized
ModuleNotFoundError: No module named 'encodings'
Current thread 0x00007464fa539740 (most recent call first):
<no Python frame>
This seems to be a regression introduced in 3.11 (my py3.12 works because it sees the standard library of my system Python).
Right, if you did not build with --enable-shared, it will static link libpython. Are you moving or copying the python binary to a different location? Or rather, how exactly are you installing Python?
Python is installed by using configure to set a prefix and using make altinstall, then manually symlinking to the altinstalled binary from /usr/local/bin. At least, that's what my notes say to do; I'll try to make time this week to redo it from scratch and confirm.
Is there still interested in a change to getpath.py to address this? I'd be happy to create a PR for discussion.
It's unclear to me what would be changed, but feel free to open a PR. Just beware that touching getpath.py is always very delicate, and there's a big possibility that whatever you change will slightly change its behavior in ways other use-cases may not be expecting, and break them. This is why I am always so cautious when touching getpath 😅
I believe a lot of them are, IIRC the getpath tests are pretty extensive. If you change something, most likely some test will fail, and we'll have to carefully consider the behavior change.
I've come back to this issue after some time and put up a proposed fix with test cases in #156483.
Currently, getpath.py searches for landmark files relative to the home value in pyvenv.cfg. When that value refers to an symlink, discovery can fall back to the prefix value set during the build. If the install does not match this location, either from relocating after and install or installing into a different location, then sys.base_prefix, etc are set incorrectly. This is the case that occured n python-build-standalone as the distribution are almost never installed into the configured prefix, /install.
Additionally, if an landmark file exist near the symlink, perhaps from another valid python installation, these can result in the symlinked directory being identified as the base installation.
Both of these can cases be reproduced with Docker containers:
Activity
mayeut commentedon Jan 14, 2024
I've run into this as well:
Python built from source & installed in /opt/python-3.11, symlink in /usr/local/bin/python3.11.
In my case, it was on ubuntu 22.04, the venv could not be created with a failure to
import subprocessbecause indeed it was trying to use/usr/lib/python3.11forlib-dynloadinsys.path.Changing
homemanually or patching thevenvmodule worked.reproducer in GHA: https://github.com/mayeut/sandbox/actions/runs/7518304095/job/20465487706#step:3:81
pythongh-106045: Fix ``venv`` creation from a python executable symlink
pythongh-106045: Fix ``venv`` creation from a python executable symlink
cjolowicz commentedon Mar 3, 2024
Note that resolving symlinks will break Homebrew, see astral-sh/uv#1640
konstin commentedon Mar 3, 2024
Do you have instructions how to reproduce this? We'd like to avoid this problem in uv.
From
docker run --rm debian:12Then
venv/bin/python -c "import struct; import subprocess"works fine for me.Same questions, do you have instructions how to reproduce this (outside the github actions one)? I tried this with a python checkout (4d3ee77):
Then i tried:
But this all passed.
mayeut commentedon Mar 4, 2024
@konstin,
On ubuntu 22.04, install
sudo apt-get install -y python3-distutils, create a symlink to your python3.11 build in /usr/local/bin/python3.11.I think the fact that the symlink is in
/usr/local/binfolder matters. Just creating a symlink elsewhere does not necessarily reveals the issue.konstin commentedon Mar 4, 2024
Do you have more information about where the python3.11 came from? I still can't reproduce
mayeut commentedon Mar 4, 2024
I could reproduce it using your dockerfile with a slight modification.
The folder
/usr/lib/python3.11/lib-dynloadshould exist for the venv creation to fail.You can mkdir the folder or also add
python3-gdbmto the list of packages to install.konstin commentedon Mar 4, 2024
To summarize, the error only happens (editing this to 3.13 to be actionable on main) when you have a custom built python that is linked to exactly
/usr/local/bin/python3, but there's also a system python of the same version installed in/usrinstalled that created/usr/lib/python3.13/lib-dynload?I've tried to trace the cause and it seems that
installed_platbasegets switched to/usron venv creation:This in turn changes
platincludeto the wrong python:I unfortunately don't understand enough about the python sysconfig machinery to understand how/why
installed_platbaseis set.Note that for the system (ubuntu
apt install python3.11 python3.11-venv) python, all paths are (correctly)/usrbased and stay this way:cjolowicz commentedon Mar 4, 2024
I haven't looked into this in detail, but might the real issue here be that you're effectively running the interpreter in a broken system-wide environment under
/usr/local? In other words, don't just symlink the interpreter. (Disclaimer: I'm not an expert in the sysconfig machinery either.)The actual command used to launch the interpreter determines what's considered the environment, even if it's a symbolic link into a valid installation. To my knowledge, sysconfig uses the nominal (unresolved) interpreter path to expand the location templates in installation schemes.(not sure about that, should double check)nascheme commentedon Mar 5, 2024
I don't know what a "broken system-wide environment" means. As I said in the description, I think this is a bug in venv since it doesn't match what getpath.c does.
Rather than having venv have complicated logic to figure out what "home" should be, perhaps it should just run something like:
python3 -c "import sysconfig; print(sysconfig.get_paths())".I've worked around the issue by creating wrapper shell scripts for executables, rather than using symlinks, e.g.
usr/local/bin/python3contains:pythongh-106045: Fix ``venv`` creation from a python executable symlink
pythongh-106045: Fix ``venv`` creation from a python executable symlink
5 remaining items
pythongh-106045: Fix ``venv`` creation from a python executable symlink
mayeut commentedon Jan 11, 2025
It does not seem to be helping in this case (on macOS):
With #115237 resolving
homedifferently, the venv works with both shared & static builds.mayeut commentedon Jan 16, 2025
@pelson, did you mean the following instead ?
zahlman commentedon Mar 28, 2025
I frequently encounter this problem because of how I've set up my multiple local builds of Python (used to test version compatibility for packages, and also to investigate differences between Python versions). Because of the wrong home path, the standard library can't be found at all, with more dramatic consequences:
This seems to be a regression introduced in 3.11 (my
py3.12works because it sees the standard library of my system Python).FFY00 commentedon Mar 28, 2025
Can you run
ldd py3.13? If libpython is linked, please also let me know if the linker is loading it from the correct place.zahlman commentedon Mar 29, 2025
Not there. I just used all default compilation options for these builds.
But it looks the same for the ones where it works.
FFY00 commentedon Mar 30, 2025
Right, if you did not build with
--enable-shared, it will static link libpython. Are you moving or copying thepythonbinary to a different location? Or rather, how exactly are you installing Python?zahlman commentedon Mar 31, 2025
Python is installed by using
configureto set a prefix and usingmake altinstall, then manually symlinking to the altinstalled binary from/usr/local/bin. At least, that's what my notes say to do; I'll try to make time this week to redo it from scratch and confirm.jjhelmus commentedon Oct 31, 2025
I ran into this issue in python-build-standalone, astral-sh/python-build-standalone#380
Is there still interested in a change to
getpath.pyto address this? I'd be happy to create a PR for discussion.brettcannon commentedon Oct 31, 2025
@jjhelmus sure, let's see what a PR looks like.
FFY00 commentedon Dec 10, 2025
It's unclear to me what would be changed, but feel free to open a PR. Just beware that touching
getpath.pyis always very delicate, and there's a big possibility that whatever you change will slightly change its behavior in ways other use-cases may not be expecting, and break them. This is why I am always so cautious when touchinggetpath😅reneleonhardt commentedon Dec 10, 2025
Would it be possible to add all those untested use cases / branches to the unit test coverage before changing
getpath?FFY00 commentedon Dec 10, 2025
I believe a lot of them are, IIRC the
getpathtests are pretty extensive. If you change something, most likely some test will fail, and we'll have to carefully consider the behavior change.jjhelmus commentedon Aug 27, 2026
I've come back to this issue after some time and put up a proposed fix with test cases in #156483.
Currently,
getpath.pysearches for landmark files relative to the home value inpyvenv.cfg. When that value refers to an symlink, discovery can fall back to the prefix value set during the build. If the install does not match this location, either from relocating after and install or installing into a different location, then sys.base_prefix, etc are set incorrectly. This is the case that occured npython-build-standaloneas the distribution are almost never installed into the configured prefix,/install.Additionally, if an landmark file exist near the symlink, perhaps from another valid python installation, these can result in the symlinked directory being identified as the base installation.
Both of these can cases be reproduced with Docker containers:
Result:
And the misleading landmark file:
Result:
Both of these layouts work as expected with the C-based path-discovery in Python 3.10:
Relocated Python
Results:
Landmark