Support copy or hardlink python interpreter when creating venv
Currently, when creating a venv, uv always symlinks the python interpreter. Example:
$ uv venv --python=3.12 /venv
$ ls -l /venv/bin
...
lrwxrwxrwx 1 root root 25 Aug 28 23:53 python -> /root/.local/share/uv/python/cpython-3.12.5-linux-aarch64-gnu/bin/python3.12
lrwxrwxrwx 1 root root 6 Aug 28 23:53 python3 -> python
lrwxrwxrwx 1 root root 6 Aug 28 23:53 python3.12 -> python
The original python -m venv supports either symlink or copy (with --symlinks / --copies flag). It would be great if uv also supported the same, similar to how --link-mode exists for uv pip.
My main use case is for using a docker multi-stage build where one of the stages builds the venv and then other stages can just copy the entire venv folder; example Dockerfile:
FROM base as venv-builder
RUN uv venv --python=3.12 /venv
RUN uv pip install --python /venv/bin/python ...
FROM base as final-image
# Do other things here
COPY --from=venv-builder /venv /venv
This use case currently breaks as the symlink ends up pointing to a python binary that does not exist in the final image. For this use case, the ideal behavior would be that the main /venv/bin/python binary was copied or hardlinked from the external binary and python3/python3.12 continued to be relative symlinks.
Tested on uv version 0.4.0.
This makes sense to me.
Minor note, this is included as --bundle-python in @konstin's document about features we should add for Docker.
Sometimes, I want to package the Python source code and the virtual environment (venv) together for direct distribution. In this case, the interpreter should not be a symbolic link, but a full copy of the original interpreter. and I hope to have an option for this. Currently, I am using a mix of conda and pip. After this feature is added in the future, I plan to replace them with uv.
Running into this also - basically the docker case but the applications current make use of python -m venv .bundle --copies and I'm not seeing any alternative apart from this issue.
I think the alternative is you just copy over the interpreter along with the environment.
any updates here? Would really like to have this functionality!
Me as well, please 💟
We use the official Docker images to setup Python environments with 'uv'.
But later on, we need to run the actual scripts under a different Docker image.
The way with having a combined image (donor 'uv' executable) does not really scale, as you would need to build one image per combination of Python interpreter version and Linux distro 🙈 .
+1 for this, for a different use case. Using uv virtual environments with Cursor, Cursor appears to follow the symlink and give me the wrong environment. I'm also following up to see if it's something I can fix within Cursor, but I've had no luck so far.
+1 for this, for a different use case. Using uv virtual environments with Cursor, Cursor appears to follow the symlink and give me the wrong environment. I'm also following up to see if it's something I can fix within Cursor, but I've had no luck so far.
Same in vscode, at least for me.
Now with uv venv --relocatable, the last obstacle to a copiable virtual environment is base python's installation dir: python executable symlink's path and home field in pyvenv.cfg.
I attempted to address it in commit https://github.com/astral-sh/uv/commit/b79ab015e40f3812ee138f5180a22d4873cb6449, to use relative path for python symlink and home field, when uv venv is provided with:
- Relocatable mode:
--relocatable - A relative path to python installation:
--python <../relative/path/to/python>
The python executable does not bring issue, however home in pyvenv.cfg does: when home path is relative, it is relative to python process's working directory, instead of the location of pyvenv.cfg, see https://github.com/python/cpython/issues/83650.
This is essentially a deal breaker. One workaround proposed in Stack Overflow suggests to modify bin/activate script to update home field when activating the environment. This may work in most circumstances, but cannot be proposed as a general solution for uv. I wonder whether home field here will still be an issue when implementing the feature in this issue.
Support for relative home path in pyvenv.cfg is now being discussed in PEP 796:
- https://github.com/python/peps/pull/4476
- https://github.com/python/cpython/issues/136051
Once it gets accepted, I can make a PR for https://github.com/Vigilans/uv/commit/b79ab015e40f3812ee138f5180a22d4873cb6449 to make uv venv fully portable when provided with relative python path in relocatable mode.
(As for the original request in this issue, python -m venv --copies seems to be a no-op in non-windows platform, and there seems to be more things than hardlinking/copying python binary alone in a python installation (e.g. include, lib, and share folder in python-build-standalone)
noting that this issue covers the same ground as #2103
I got what I wanted with some jank: I installed a python and pretended it was a venv
$ uv python install 3 -i .
$ mv cpython-3.13.7-linux-aarch64-gnu .venv
$ uv pip install jinja2
error: Broken virtual environment `/home/raylu/test/.venv`: `pyvenv.cfg` is missing
$ uv sync
+ jinja2==3.1.6
$ ls -1 .venv/lib/python3.13/site-packages
jinja2
jinja2-3.1.6.dist-info
so uv pip install thinks it's not a functional venv but uv sync is still happy to install my pyproject.toml's dependencies. and there are no symlinks out of the directory so it works as a chroot
(As for the original request in this issue,
python -m venv --copiesseems to be a no-op in non-windows platform, and there seems to be more things than hardlinking/copying python binary alone in a python installation (e.g. include, lib, and share folder in python-build-standalone)
This is false. On my Linux system if I run without --copies I get this in bin
lrwxrwxrwx 1 490 490 38 Oct 14 16:04 python -> /usr/lib/python-exec/python3.13/python
lrwxrwxrwx 1 490 490 6 Oct 14 16:04 python3 -> python
lrwxrwxrwx 1 490 490 6 Oct 14 16:04 python3.13 -> python
But with --copies I get this:
-rwxr-xr-x 1 490 490 14344 Oct 14 16:05 python
-rwxr-xr-x 1 490 490 14344 Oct 14 16:05 python3
-rwxr-xr-x 1 490 490 14344 Oct 14 16:05 python3.13
I'm not sure what OP's issue was in that SO post, but seems like it might have been related to lib vs lib64 on his machine, not bin. Then commenter un-helpfully misinterpreted the python source code. A closer read will show that --copies is a no-op on Windows platforms rather than non-Windows platforms.
There is still great merit in the original request.
For anyone looking for a workaround...
Running
uv venv --clear && find .venv -type l -exec sh -c 'target=$(readlink -f "$1"); rm "$1"; cp -a "$target" "$1"' _ {} \;
instead of just
uv venv --clear
does the trick