Portable entry points

Reference: Add installation option for portable entry points · Issue #3669 · astral-sh/uv · GitHub

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

There is a question in the UV Discord that I don’t know how to answer because I wasn’t around at the time:

My main question is why it isn’t the default in the spec

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.

Changing the default behaviour would be a massive compatibility break. Having an option to use a relative path to the environment as a non-default alternative is, I guess, a possibility. My feeling is that it could cause a lot of confusion, though.

(I’ll add this information to the uv issue as well, for completeness)

2 Likes

It’s not clear to me why this is supposed to cause a problem. After all, said absolute path is being computed by the installer, according to where the entry point will be set up, right?

This is an issue because the end-users do not have the same file system structure as the builders and there is no way to enforce them unpacking at a certain path per platform.

Wait, since when are these entry points unpacked from the wheel? I thought they were generated by the installer from metadata.

1 Like

I’m taking one of these: Releases · indygreg/python-build-standalone · GitHub

I then unpack it, install a bunch of packages, then archive again to create the final distribution.