-
-
Notifications
You must be signed in to change notification settings - Fork 157
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Creating venvs via standard library's venv.create(..., with_pip=True)
fails for binaries linked against libpython dynamically
#381
Comments
Maybe helps
Adding LD_LIBRARY_PATH works so...
|
This is a little bit different from astral-sh/uv#6812. While they're both sort of about You can repro this with the CLI tool:
The reason is that Some fun facts about how other distributions of Python approach this:
$ diff -ur <(git -C src/cpython show v3.9.0:Lib/venv/__init__.py) /Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/venv/__init__.py
--- /dev/fd/63 2025-01-27 15:22:52
+++ /Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/venv/__init__.py 2024-11-11 06:18:32
@@ -16,6 +16,21 @@
CORE_VENV_DEPS = ('pip', 'setuptools')
logger = logging.getLogger(__name__)
+def should_use_symlinks(symlinks=None):
+ if symlinks:
+ return True
+ else:
+ # The python we build for Xcode uses @executable_path/../Python3 to find Python3.framework
+ # A venv created without symlinks will not be able to load the framework, because
+ # @executable_path would be in the venv, not in the original location within Xcode.
+ symlinks_are_required = sysconfig.get_config_var('TRAIN_STYLE') == 'DT'
+ if symlinks is None:
+ return symlinks_are_required
+ else:
+ if symlinks_are_required:
+ raise Exception("This build of python cannot create venvs without using symlinks")
+ else:
+ return symlinks
class EnvBuilder:
"""
@@ -44,11 +59,11 @@
"""
def __init__(self, system_site_packages=False, clear=False,
- symlinks=False, upgrade=False, with_pip=False, prompt=None,
+ symlinks=None, upgrade=False, with_pip=False, prompt=None,
upgrade_deps=False):
self.system_site_packages = system_site_packages
self.clear = clear
- self.symlinks = symlinks
+ self.symlinks = should_use_symlinks(symlinks)
self.upgrade = upgrade
self.with_pip = with_pip
if prompt == '.': # see bpo-38901
@@ -404,7 +419,7 @@
def create(env_dir, system_site_packages=False, clear=False,
- symlinks=False, with_pip=False, prompt=None, upgrade_deps=False):
+ symlinks=None, with_pip=False, prompt=None, upgrade_deps=False):
"""Create a virtual environment in a directory."""
builder = EnvBuilder(system_site_packages=system_site_packages,
clear=clear, symlinks=symlinks, with_pip=with_pip, (I have no idea what |
…s with a standalone Python (#505) Per #381 (comment) this should stop the build bootsrapping script from crashing when running the build with a standalone Python distribution (which was.. really annoying me).
#505 only mitigates the issue, instead of fixing the source. I feel like the proper fix should be around setting If the base installation itself is actually meant to be relocatable (as-in it may be moved around), then we might want to warn users about its fragility when creating virtual environments. |
To be clear #505 is just working around this issue for this repository's own Python code in the build scripts (when the Python interpreter itself is from python-build-standalone)—it's not intended as a fix for the outputs of this repository / does not change the outputs. I'm inclined to say that copy-based venvs are just unsupported for python-build-standalone (as they are for Xcode's Pythons), but yeah, having |
Does that realistically matter in systems where symlinks are available? Copy-based venvs exist primarily for Windows, where symlink support is problematic. Are there any use-cases for copy-based venvs in systems like macOS, which implement the kind of code signing you mentioned? If not, I think the best solution is to simply adjust the UX so that users don't run into these scenarios by mistake. We could fix the issue with a more complex solution like requiring certain tooling to set the |
Just to share more context, people also want fully portable virtual environments, e.g.,
but I think that's distinct from a typical copy-based venv. |
Even with I don't know of any use case on UNIX actually wants copy-based venvs (as I argued in python/cpython#129382). My only hesitation was that it's a thing the code currently supports and currently defaults to, but I would be more than happy to break it and say symlinks only. Apple seems to have gotten away with doing that. |
Just to clarify, by fully portable virtual environments, you mean virtual environments that can be moved around, right? The former is shouldn't be a problem here, the second should be handled differently from a "venv" as you need a full Python distribution, which has some implications for the Python path/prefix initialization.
Yup, exactly. This is no longer a virtual environment or, at least, not a "lightweight virtual environment", which is what I think that is a desirable feature, but |
Yeah, I agree — that's what I meant when I said it was distinct. I think we're all on the same page here, I was just sharing those use-cases for context on what people are asking for. I don't know of anyone that's asking for copy-based venvs on Unix. I wouldn't be surprised if something came up if we banned it, but I'd be willing to give it a try here (where release iteration times are much faster than CPython). |
OK, with python/cpython#129493 happening, I propose that on the python-build-standalone side we
Does that seem reasonable? I should note for completeness that there is another option, that we (or really probably upstream CPython) copy libpython into the venv, so that the
But I think the consensus here is that this is not worth supporting. (There is also, I guess, an argument that we should do this and also for symlink-based venvs, we should symlink libpython into the venv, which would resolve astral-sh/uv#6812. #508, etc. But that seems weird because such a symlink is unnecessary and confusing for non-relocatable Python installations, and I worry it will cause people to write build scripts that work properly on python-build-standalone but not on other Python installations.) |
Just to clarify what options are available. We could also detect this case in
Definitely
A little more controversial. I'd do so in a separate pull request. It's only broken when compiling extension modules, right? In all other cases, it's fine? Like installing from pre-built wheels will always work? I can't think of a case where copying is preferable to the symlink, which makes me more willing to ban it, but given that the environments are generally usable it feels aggressive. As an alternative solution, we could just special case build failures in uv where the header is missing and the interpreter is not a symlink. |
So there's like three classes of issues that are all very similar-sounding but are not exactly the same thing:
This issue is about the interpreter not finding the runtime library. If you can't find it, then the Python interpreter cannot start up and is completely broken. The reason for this is that it references its own runtime library by relative path from its binary location:
If you move/copy the binary to some location and do not preserve a The specific reason On the other hand, if you symlink the binary, then There's two ways that you can avoid caring about this problem. The first is that you build your Python interpreter so that the libpython guts are statically linked into (I suppose one thing we can do is to statically link libpython into our interpreter. I'm not sure off hand why we don't, though the argument that comes to mind is disk space.) The second problem is about compiling (or more specifically, linking) code that needs to link libpython. For those, you generally need to find a Again, a systemwide Python with the python3-dev package (or whatever your OS calls it) installed is going to have something like a The third problem is that, supposing you've gotten past the second problem and successfully compiled some program that links libpython, your program is going to need to locate the runtime library in order to run. Again, for a systemwide Python this just works. But for a relocatable Python, you either need to get an Note that this third problem is orthogonal from venvs; it's pretty unlikely that you're going to compile your own program into the same directory as So, recapping: this issue is only the first problem of that list. astral-sh/uv#6812 is sort of all three problems. It was reported by demonstrating the Python interpreter failing to find libpython under So:
Currently (AIUI) (My parenthetical proposal in the previous comment is to address the second and third problems, which do apply to symlink-based venvs. In that case, yes,
No, because this is about the first problem, where the Python interpreter is wholly broken, and
I don't think we're guaranteed to be within uv here. Even setting aside that this is the python-build-standalone repo—the issue on the uv side is about running Lemme know if this is still confusing, I can barely keep the various problems straight :) |
Great, thank you for the clarification! I'm definitely mixing problems :) With that context, banning copied interpreters for virtual environments makes sense. Regarding
The commentary in #44 (comment) and the linked commit 54bf7c6 may provide some context? Though it seems a bit different. |
Oh, the thing that confused me is that that issue/change is about whether to build libpython as static or shared, and my question was about the whether the python binary consumes libpython as static or shared. But those are ordinarily the same question. What confused me is that I'm used to Debian, where /usr/bin/python3 statically links libpython and a shared libpython also exists, but they do something unusual—they build twice, once static and once shared, and they ship the bin/python from the static build but also ship the shared library. Apparently this is for performance concerns raised 23 years ago. Weird. Fedora doesn't seem to do this. So I no longer think there's an argument that python-build-standalone should do something different here :) |
Minimal example:
Running this with a python-build-standalone binary that has a shared library dependency on
libpython*.so
, e.g.cpython-3.12.7+20241016-x86_64-unknown-linux-gnu-install_only
will make this fail with (extra newlines inserted for readability):Obviously, this is because the
python
binary links againstlibpython*.so
dynamically with a relative path, and there no longer is alibpython*.so
at this path when the Python executable (or a symlink to it) finds itself in the venv.See also the corresponding issue in uv:
The text was updated successfully, but these errors were encountered: