2025-12-13-uv 一个pyproject.toml同时支持cpu和gpu依赖
希望是在一个pyproject.toml里解决同一个python工程支持cpu和gpu环境下不同的依赖。
环境
ubuntu24 内核6.14.0-24
uv 0.9.11
背景
我有一个python工程,想同时支持cpu和gpu环境下使用,那么torch onnxruntime等需要依赖的包就不一样。不想提供多个requirements.txt,希望是在一个pyproject.toml里解决。
pyproject.toml 一开始参考网上的写法 为:
[project]
...略...
dependencies = [ # 核心通用依赖 "numpy==1.26.4", "opencv-python-headless==4.8.1.78", "Pillow==10.2.0", "scikit-image==0.22.0", "protobuf==4.25.3", "transformers==4.44.0", "sentencepiece==0.1.99", "open_clip_torch==2.24.0", "insightface==0.7.3", "onnxruntime==1.17.0", "fastapi==0.110.1", "pydantic==2.7.1", "uvicorn==0.29.0" ]
# 可选依赖(CPU/GPU)
[project.optional-dependencies]
cpu = [ "torch==2.2.2", "torchvision==0.17.2", "torchaudio==2.2.2", "onnxruntime==1.17.0" ]
gpu = [ "torch==2.2.2+cu121", "torchvision==0.17.2+cu121", "torchaudio==2.2.2", "onnxruntime-gpu==1.17.0" ]
[tool.uv.sources]
torch = [ { index = "pytorch-cpu", extra = "cpu" }, { index = "pytorch-cu124", extra = "gpu" }, ]
torchvision = [ { index = "pytorch-cpu", extra = "cpu" }, { index = "pytorch-cu124", extra = "gpu" }, ]
torchaudio = [ { index = "pytorch-cpu", extra = "cpu" }, { index = "pytorch-cu124", extra = "gpu" }, ]
[[tool.uv.index]]
name = "pytorch-cpu"
url = "https://download.pytorch.org/whl/cpu"
explicit = true
[[tool.uv.index]]
name = "pytorch-cu124"
url = "https://download.pytorch.org/whl/cu124"
explicit = true
# 镜像设置
[tool.uv]
index-url = "https://mirrors.cernet.edu.cn/pypi/web/simple"
作者:samonyu
链接:https://juejin.cn/post/7468614239940526107
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
运行结果报错(网上的--optional 参数不对):
uv sync --extra gpu
× Failed to resolve dependencies for `ai-pipeline` (v0.1.0) ╰─▶ Requirements contain conflicting indexes for package `torch` in all marker environments: - [https://download.pytorch.org/whl/cpu](https://download.pytorch.org/whl/cpu) - [https://download.pytorch.org/whl/cu121](https://download.pytorch.org/whl/cu121)
错误很明显,但gpt 豆包 qwen都没给合适的方式。
关键官方文档
从 这篇贴文 间接找到 https://docs.astral.sh/uv/reference/settings/#index 然后将其让豆包根据这个文档再来处理,有了一个接近成功的方案:
# 可选依赖(CPU/GPU 包区分,仅定义依赖,不绑定索引)
[project.optional-dependencies]
cpu = [
"torch==2.2.2", # CPU 版 PyTorch(无 CUDA 后缀)
"torchvision==0.17.2",
"torchaudio==2.2.2",
"onnxruntime==1.17.0" # CPU 版 onnxruntime]
gpu = [
"torch==2.2.2+cu121", # GPU 版 PyTorch(带 CUDA 后缀)
"torchvision==0.17.2+cu121",
"torchaudio==2.2.2",
"onnxruntime-gpu==1.17.0"# GPU 版 onnxruntime]
# UV 基础配置(仅默认 PyPI 镜像,无额外索引绑定)
[tool.uv]
index-url = "https://mirrors.cernet.edu.cn/pypi/web/simple" # 加速普通依赖
然后运行 uv sync --extra gpu \ --extra-index-url https://download.pytorch.org/whl/cu121 这个方案有个问题会使depency里的Numpy这种依赖也走这个url,导致安装失败,根据错误提示很容易知道 增加 -- index-strategy = "unsafe-best-match" 或者对应的[too.uv]下的配置即可解决。
最终方案
虽然成功了,但是还是不满意,因为觉得上述用法是让url 扩大化/错误地作用在了所有的依赖上,而用index-strategy纠正了,还是更想用最初的想法。 而且通过上文的官方文档也知道,在index里指定explicit=true就只作用在sources里配置的torch这些上,再结合前面的命令行传参的思路,尝试和提问豆包,验证后得到最终的方案:
# 可选依赖(CPU/GPU 包区分,仅定义依赖,不绑定索引)
[project.optional-dependencies]
cpu = [
"torch==2.2.2", # CPU 版 PyTorch(无 CUDA 后缀)
"torchvision==0.17.2",
"torchaudio==2.2.2",
"onnxruntime==1.17.0" # CPU 版 onnxruntime]
gpu = [
"torch==2.2.2+cu121", # GPU 版 PyTorch(带 CUDA 后缀)
"torchvision==0.17.2+cu121",
"torchaudio==2.2.2",
"onnxruntime-gpu==1.17.0"# GPU 版 onnxruntime]
# UV 基础配置(仅默认 PyPI 镜像,无额外索引绑定)
[tool.uv]
index-url = "https://mirrors.cernet.edu.cn/pypi/web/simple" # 加速普通依赖
#index-strategy = "unsafe-best-match" # 否则dependencies中的numpy这些也会走命令行中指定的--extra-index-url https://download.pytorch.org/whl/cu121
[[tool.uv.index]]
name = "pytorch"
url = "https://placeholder-for-pytorch-index" # 占位符,无实际作用
explicit = true
[tool.uv.sources]
torch = [
{ index = "pytorch" }
]
torchvision = [
{ index = "pytorch" }
]
torchaudio = [
{ index = "pytorch" }
]
然后运行 uv sync --extra gpu --index pytorch=https://download.pytorch.org/whl/cu121 搞定。
cpu版本的命令为:uv sync --extra cpu \ --index pytorch=https://download.pytorch.org/whl/cpu
注意这里的url必须写个占位地址,豆包的解释是(符合逻辑和验证,未去官网验证):
- TOML 不支持运行时变量插值(如
${tool.uv.index-url}),无法直接留空或动态引用。
ps:这里豆包说仍然需要index-strategy = "unsafe-best-match",但我认为并不需要(因为url作用范围缩到最小了),验证后确实不需要,注释掉。
过度设计?仅有的「分开装 CPU 版」的特殊场景(几乎都是边缘情况)
只有以下场景,装纯 CPU 版依赖才有微弱价值:
| 场景 | 说明 | 是否值得折腾? |
|---|---|---|
| 纯 CPU 环境部署(如云服务器无 GPU) | 服务器没有 GPU、也没装 CUDA 驱动,GPU 版依赖的「GPU 加速模块」会成为冗余,但不影响运行(只是多占一点磁盘) | 不值得:GPU 版依赖仍能正常运行 CPU 逻辑,仅多几十 MB 磁盘占用;若极度追求精简(如嵌入式设备),可装 CPU 版 |
| 依赖体积极致优化 | CPU 版依赖比 GPU 版小几十 MB~ 几百 MB(比如 torch CPU 版~400MB,GPU 版~1.2GB) | 不值得:现代环境磁盘 / 网络都不缺这点空间,除非是嵌入式 / 极简容器场景 |
| 极少数 GPU 版兼容问题 | 极个别老旧库的 GPU 版在纯 CPU 环境下可能报「找不到 CUDA 库」的警告(但不影响运行) | 不值得:只需加一行环境变量(如 CUDA_VISIBLE_DEVICES=-1)即可屏蔽警告,无需换依赖 |
| 严格的合规 / 审计要求 | 环境不允许包含未使用的 GPU 模块(如部分政企场景) | 视合规要求而定,属于极特殊情况 |