fix(cookbook): skip pip --user fallback inside virtualenvs (#388) (#889)

The dependency-install fallback chain unconditionally ran
'pip install --user', which fails inside a virtualenv (and as root in
LXC/containers) with 'Can not perform a --user install. User site-packages
are not visible in this virtualenv.' — even though the function's docstring
already noted --user is invalid in venvs.

Guard the --user fallback with a venv check so it only runs outside a venv
(where --user is actually valid for PEP-668 system Pythons). Derive the venv
probe interpreter from the install command (python for 'pip', python3 for
'pip3'/'python3 -m pip') so the check runs in pip's own environment. System
PEP-668 installs keep the --user fallback; venv/LXC-root installs no longer
hit the --user error. Updated the unit test for the new chain.

Closes #388
This commit is contained in:
Tatlatat
2026-06-02 10:23:20 +07:00
committed by GitHub
parent 966b53df77
commit 9a1893760d
2 changed files with 18 additions and 5 deletions

View File

@@ -152,13 +152,25 @@ def _pip_install_fallback_chain(package: str, *, python_cmd: str = "python3 -m p
"""Build a bash pip install fallback chain.
Try the active interpreter/environment first. `--user` is invalid inside
many venvs, so keep the user-site fallback for PEP-668 system Pythons only
after the venv-safe attempt has failed.
many venvs, so only attempt the --user fallback when NOT inside a venv.
"""
upgrade_flag = " -U" if upgrade else ""
base = f"{python_cmd} install -q{upgrade_flag} {package} 2>/dev/null"
user = f"{python_cmd} install --user --break-system-packages -q{upgrade_flag} {package} 2>/dev/null"
return f"{base} || {user}"
# Derive the python executable for the venv detection check.
# Must use the same interpreter that pip belongs to; hardcoding
# python3 breaks when pip lives in a venv that only has "python".
if " -m pip" in python_cmd:
python_exe = python_cmd.replace(" -m pip", "")
elif python_cmd.strip() == "pip":
python_exe = "python"
elif python_cmd.strip() == "pip3":
python_exe = "python3"
else:
python_exe = "python3"
venv_check = f'{python_exe} -c "import sys; sys.exit(0 if sys.prefix != sys.base_prefix else 1)"'
# venv_check exits 0 (true) when IN a venv; --user is only valid outside one.
return f"{base} || {{ {venv_check} || {user}; }}"
def _cached_model_scan_script(model_dirs: list[str] | None = None) -> str:

View File

@@ -92,8 +92,9 @@ def test_pip_install_fallback_chain_prefers_venv_safe_install():
def test_pip_install_fallback_chain_allows_custom_python_command():
chain = _pip_install_fallback_chain("hf_transfer", python_cmd="pip", upgrade=False)
assert chain == (
"pip install -q hf_transfer 2>/dev/null || "
"pip install --user --break-system-packages -q hf_transfer 2>/dev/null"
'pip install -q hf_transfer 2>/dev/null || { '
'python -c "import sys; sys.exit(0 if sys.prefix != sys.base_prefix else 1)"'
' || pip install --user --break-system-packages -q hf_transfer 2>/dev/null; }'
)