astral-sh / uv

An extremely fast Python package and project manager, written in Rust.
https://docs.astral.sh/uv
Apache License 2.0
25.68k stars 750 forks source link

Add installation option for portable entry points #3669

Open ofek opened 5 months ago

ofek commented 5 months ago

I have a use case where I need to pre-build an entire installation which will be distributed to any number of machines. When installing packages that have entry points, the location becomes hardcoded to an absolute path.

Instead, this should be relative to the Python executable used for installation. For non-Windows systems the python-build-standalone project does this for pip for example:

#!/bin/sh
"exec" "$(dirname $0)/python3.12" "$0" "$@"
# -*- coding: utf-8 -*-
import re
import sys
from pip._internal.cli.main import main
if __name__ == '__main__':
    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
    sys.exit(main())

The trampoline logic should be equivalently simple for Windows.

Maybe this should become the default in future?

pfmoore commented 5 months ago

This definitely should not be the default. Entry point wrappers are built with absolute paths to the interpreter so that the wrapper can be copied or symlinked to other locations, without needing to copy the Python environment as well. This is something that's used by a lot of applications - most notably pipx, which symlinks ~/.local/bin/appname.exe to ~/.local/pipx/venvs/appenv/Scripts/appname.exe.

charliermarsh commented 5 months ago

Makes sense, thanks for that context @pfmoore.

skoslowski commented 5 months ago

I have a similar use-case. It would be great to be able to skip patching wrappers post-install.

(On Windows-systems I am using <launcher_dir> for distlib-based launchers)

ofek commented 5 months ago

FYI since https://github.com/astral-sh/uv/pull/3717 this is now completely possible to implement and I do so here:

pfmoore commented 5 months ago

I'm confused as to what's happening here. I could test, but I have limited time right now so if someone can explain, I'd be grateful. If I uv pip install a project that has a script entry point, does that entry point by default still use an absolute path to the Python executable binary?

charliermarsh commented 5 months ago

Yeah. We didn’t change any behavior in uv itself, except we changed our Windows trampoline to accept a relative path (though we never write one to it in uv). Ofek is then post-processing the scripts in Hatch, I think, to rewrite the absolute paths as relative.

ofek commented 5 months ago

Yes that is exactly correct and I can remove that entire logic once such a flag exists in UV! I posted those links mostly to assist in someone doing the implementation.