You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何实现Python包支持依赖的不同变体(如CPU/GPU版)且避免冲突安装?

如何实现Python包支持依赖的不同变体(如CPU/GPU版)且避免冲突安装?

我完全理解你的痛点——想通过额外依赖(extra)让用户一键切换依赖的CPU/GPU变体,但pip的默认解析逻辑却导致两个冲突变体同时安装,这确实很头疼。下面给你几个比发布两个独立包更优的方案:


方案1:使用带Extra标记的条件依赖(推荐,适配现代pip)

你的尝试方向其实是对的,只是可能因为pip版本过旧导致标记不生效。现代pip(20.3+,推荐21.0+)已经能正确解析基于extra的条件依赖,不会同时安装冲突的包。

在你的pyproject.toml里这样配置:

[project]
name = "A"
version = "0.1.0"
# 仅当未指定cuda extra时,才安装onnxruntime
dependencies = [
  "onnxruntime; extra != 'cuda'",
  "pip>=21.0"  # 强制用户使用支持条件标记的现代pip
]

[project.optional-dependencies]
# 指定cuda extra时,安装onnxruntime-gpu
cuda = [
  "onnxruntime-gpu"
]

为什么之前没生效?

  • 如果你用的是pip 20.3之前的旧版本,它的依赖解析器不会正确处理extra标记,会无视条件安装所有依赖。升级pip到21.0+就能解决这个问题。
  • 确认你安装时的命令是pip install A[cuda],不要出现拼写错误(比如标记里用cuda但安装时写A[gpu])。

验证效果:

  • 执行pip install A:只会安装onnxruntime和包A。
  • 执行pip install A[cuda]:只会安装onnxruntime-gpu和包A,不会安装onnxruntime

方案2:核心包+元包分离(兼容旧版pip)

如果你的用户可能还在使用旧版pip,不想强制升级,可以把代码和依赖配置分离:

  1. 发布核心包A-core:包含所有业务代码,不依赖onnxruntime的任何变体(或仅依赖无冲突的基础依赖)。
  2. 发布元包A:作为入口,通过不同extras拉取正确的核心包和依赖变体:
# 元包A的pyproject.toml
[project]
name = "A"
version = "0.1.0"
dependencies = ["A-core", "onnxruntime"]

[project.optional-dependencies]
cuda = [
  "A-core",
  "onnxruntime-gpu",
  "onnxruntime==0.0.0; python_version>='3.7'"  # 虚拟版本,强制pip忽略默认的onnxruntime
]

说明:

  • 元包本身没有代码,仅负责拉取正确的核心包和依赖。
  • 虚拟版本onnxruntime==0.0.0是一个小技巧,会让pip优先选择onnxruntime-gpu(因为它是有效版本),从而跳过默认的onnxruntime

方案3:动态依赖配置(适合复杂场景)

如果需要更灵活的逻辑,可以用setuptools的动态依赖功能,在setup.py里根据用户指定的extras动态设置依赖:

from setuptools import setup

def get_dependencies(extras):
    if 'cuda' in extras:
        return ["onnxruntime-gpu"]
    return ["onnxruntime"]

setup(
    name="A",
    version="0.1.0",
    install_requires=get_dependencies(set()),
    extras_require={
        "cuda": []
    },
    dynamic=["install_requires"]
)

注意:

  • 这种方式需要配合setup.py使用,且要求setuptools>=61.0支持动态字段。
  • 需注意pip解析extras时的逻辑,建议在测试环境验证后再发布。

不推荐的方案

  • 发布AA-gpu两个独立包:虽然可行,但会大幅增加维护成本,用户也容易混淆变体。
  • 用post-install脚本卸载冲突包:属于hacky做法,可能破坏pip的依赖树,导致后续安装出现未知问题。

总结

优先选择方案1:只要用户使用现代pip,就能完美实现你想要的效果——默认安装CPU版,加extra安装GPU版,且不会出现冲突。如果必须兼容旧版pip,再考虑方案2的核心包+元包模式。

火山引擎 最新活动