mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-06-03 07:26:51 +00:00
fix(uv-pip-compat): bind venv python for more pip commands and add tests for compatibility wrapper
This commit is contained in:
@@ -27,7 +27,8 @@ fi
|
||||
has_environment_option() {
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
-p|--python|--python=*|-p*|--system)
|
||||
-p|--python|--python=*|-p*|--system|--user|\
|
||||
-t|--target|--target=*|-t*|--prefix|--prefix=*)
|
||||
return 0
|
||||
;;
|
||||
--)
|
||||
@@ -39,6 +40,18 @@ has_environment_option() {
|
||||
return 1
|
||||
}
|
||||
|
||||
uv_pip_with_venv_python() {
|
||||
command_name="$1"
|
||||
shift
|
||||
|
||||
if [ -x "${SCRIPT_DIR}/python" ] && ! has_environment_option "$@"; then
|
||||
# uv 不会仅凭 pip 软链接位置锁定 venv,本地安装也不会激活 venv。
|
||||
# 因此需要在会读取或改写环境的 pip 子命令上显式绑定当前 venv 解释器。
|
||||
exec "${UV_BIN}" pip "${command_name}" --python "${SCRIPT_DIR}/python" "$@"
|
||||
fi
|
||||
exec "${UV_BIN}" pip "${command_name}" "$@"
|
||||
}
|
||||
|
||||
case "${COMMAND_NAME}" in
|
||||
pip|pip3|pip3.*)
|
||||
if [ "$#" -eq 0 ]; then
|
||||
@@ -53,13 +66,10 @@ case "${COMMAND_NAME}" in
|
||||
shift
|
||||
exec "${UV_BIN}" help pip "$@"
|
||||
;;
|
||||
check)
|
||||
if [ -x "${SCRIPT_DIR}/python" ] && ! has_environment_option "$@"; then
|
||||
# uv 不会仅凭 pip 软链接位置锁定 venv,显式绑定当前运行态解释器。
|
||||
shift
|
||||
exec "${UV_BIN}" pip check --python "${SCRIPT_DIR}/python" "$@"
|
||||
fi
|
||||
exec "${UV_BIN}" pip "$@"
|
||||
check|freeze|install|list|show|sync|tree|uninstall)
|
||||
pip_command="$1"
|
||||
shift
|
||||
uv_pip_with_venv_python "${pip_command}" "$@"
|
||||
;;
|
||||
*)
|
||||
exec "${UV_BIN}" pip "$@"
|
||||
@@ -70,7 +80,7 @@ case "${COMMAND_NAME}" in
|
||||
exec "${UV_BIN}" pip compile "$@"
|
||||
;;
|
||||
pip-sync)
|
||||
exec "${UV_BIN}" pip sync "$@"
|
||||
uv_pip_with_venv_python sync "$@"
|
||||
;;
|
||||
*)
|
||||
echo "不支持的 pip 兼容命令入口:${COMMAND_NAME}" >&2
|
||||
|
||||
79
tests/test_uv_pip_compat.py
Normal file
79
tests/test_uv_pip_compat.py
Normal file
@@ -0,0 +1,79 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
WRAPPER = ROOT / "scripts" / "uv-pip-compat.sh"
|
||||
|
||||
|
||||
class UvPipCompatTests(unittest.TestCase):
|
||||
def run_wrapper(self, link_name: str, *args: str) -> list[str]:
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
venv_bin = Path(temp_dir) / "venv" / "bin"
|
||||
venv_bin.mkdir(parents=True)
|
||||
(venv_bin / "python").write_text("#!/bin/sh\nexit 0\n", encoding="utf-8")
|
||||
(venv_bin / "python").chmod(0o755)
|
||||
|
||||
argv_file = Path(temp_dir) / "argv.txt"
|
||||
uv_bin = venv_bin / "uv"
|
||||
uv_bin.write_text(
|
||||
"#!/bin/sh\n"
|
||||
# 测试只关心兼容层传给 uv 的参数,逐行记录可以避免 shell 转义差异干扰断言。
|
||||
f"for arg in \"$@\"; do printf '%s\\n' \"$arg\" >> '{argv_file}'; done\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
uv_bin.chmod(0o755)
|
||||
|
||||
wrapper_path = venv_bin / "uv-pip-compat"
|
||||
shutil.copy2(WRAPPER, wrapper_path)
|
||||
wrapper_path.chmod(0o755)
|
||||
|
||||
link_path = venv_bin / link_name
|
||||
link_path.symlink_to(wrapper_path.name)
|
||||
|
||||
subprocess.run(
|
||||
[str(link_path), *args],
|
||||
check=True,
|
||||
env={
|
||||
**os.environ,
|
||||
"PATH": f"{venv_bin}{os.pathsep}{os.environ.get('PATH', '')}",
|
||||
},
|
||||
)
|
||||
return argv_file.read_text(encoding="utf-8").splitlines()
|
||||
|
||||
def test_pip_install_binds_venv_python(self):
|
||||
argv = self.run_wrapper("pip", "install", "-r", "requirements.txt")
|
||||
|
||||
self.assertEqual(
|
||||
[
|
||||
"pip",
|
||||
"install",
|
||||
"--python",
|
||||
argv[3],
|
||||
"-r",
|
||||
"requirements.txt",
|
||||
],
|
||||
argv,
|
||||
)
|
||||
self.assertTrue(argv[3].endswith("/venv/bin/python"))
|
||||
|
||||
def test_pip_install_keeps_explicit_environment(self):
|
||||
argv = self.run_wrapper("pip", "install", "--system", "demo-package")
|
||||
|
||||
self.assertEqual(["pip", "install", "--system", "demo-package"], argv)
|
||||
|
||||
def test_pip_sync_binds_venv_python(self):
|
||||
argv = self.run_wrapper("pip-sync", "requirements.txt")
|
||||
|
||||
self.assertEqual(["pip", "sync", "--python", argv[3], "requirements.txt"], argv)
|
||||
self.assertTrue(argv[3].endswith("/venv/bin/python"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user