Refactor find_uv_bin and add a better error message (#14182)

Follows https://github.com/astral-sh/uv/pull/14181

Two goals here

- Remove duplicated logic and make the search order clear
- Resolve user confusion around the searched directories; we previously
only displayed the last attempt, which we rarely expect to be relevant
This commit is contained in:
Zanie Blue 2025-08-07 15:10:38 -05:00 committed by GitHub
parent a7c4634243
commit b1a036ccf5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 119 additions and 66 deletions

View file

@ -5,36 +5,38 @@ import sys
import sysconfig
class UvNotFound(FileNotFoundError): ...
def find_uv_bin() -> str:
"""Return the uv binary path."""
uv_exe = "uv" + sysconfig.get_config_var("EXE")
# Search in the scripts directory for the current prefix
path = os.path.join(sysconfig.get_path("scripts"), uv_exe)
if os.path.isfile(path):
return path
targets = [
# The scripts directory for the current Python
sysconfig.get_path("scripts"),
# The scripts directory for the base prefix (if different)
sysconfig.get_path("scripts", vars={"base": sys.base_prefix}),
# The user scheme scripts directory, e.g., `~/.local/bin`
sysconfig.get_path("scripts", scheme=_user_scheme()),
# Adjacent to the package root, e.g. from, `pip install --target`
os.path.join(os.path.dirname(os.path.dirname(__file__)), "bin"),
]
# If in a virtual environment, also search in the base prefix's scripts directory
if sys.prefix != sys.base_prefix:
path = os.path.join(
sysconfig.get_path("scripts", vars={"base": sys.base_prefix}), uv_exe
)
seen = []
for target in targets:
if target in seen:
continue
seen.append(target)
path = os.path.join(target, uv_exe)
if os.path.isfile(path):
return path
# Search in the user scheme scripts directory, e.g., `~/.local/bin`
path = os.path.join(sysconfig.get_path("scripts", scheme=_user_scheme()), uv_exe)
if os.path.isfile(path):
return path
# Search in `bin` adjacent to package root (as created by `pip install --target`).
pkg_root = os.path.dirname(os.path.dirname(__file__))
target_path = os.path.join(pkg_root, "bin", uv_exe)
if os.path.isfile(target_path):
return target_path
raise FileNotFoundError(path)
raise UvNotFound(
f"Could not find the uv binary in any of the following locations:\n"
f"{os.linesep.join(f' - {target}' for target in seen)}\n"
)
def _user_scheme() -> str: