uv icon indicating copy to clipboard operation
uv copied to clipboard

Support copy or hardlink python interpreter when creating venv

Open amartani opened this issue 1 year ago • 2 comments

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.

amartani avatar Aug 29 '24 00:08 amartani

This makes sense to me.

charliermarsh avatar Aug 29 '24 00:08 charliermarsh

Minor note, this is included as --bundle-python in @konstin's document about features we should add for Docker.

zanieb avatar Aug 29 '24 03:08 zanieb

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.

SethWen avatar Sep 12 '24 09:09 SethWen

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.

mweislley avatar Oct 25 '24 17:10 mweislley

I think the alternative is you just copy over the interpreter along with the environment.

charliermarsh avatar Oct 25 '24 17:10 charliermarsh

any updates here? Would really like to have this functionality!

RayyanRiaz avatar Nov 15 '24 17:11 RayyanRiaz

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 🙈 .

MarijkeStein avatar Jan 07 '25 09:01 MarijkeStein

+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.

bennyweise avatar Jan 12 '25 05:01 bennyweise

+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.

kaytwo avatar Jan 24 '25 18:01 kaytwo

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.

Vigilans avatar Feb 14 '25 12:02 Vigilans

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)

Vigilans avatar Jul 28 '25 15:07 Vigilans

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

raylu avatar Oct 07 '25 05:10 raylu

(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)

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.

staticglobal avatar Oct 14 '25 16:10 staticglobal

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

cyber-barrista avatar Nov 16 '25 01:11 cyber-barrista