bigywater.tech
Python · 34

pip 安装依赖:依赖解析和版本冲突怎么发生

真实打包4个互相版本冲突的本地wheel,对比分步pip install和合并pip install在同一个冲突下给出的真实不同结果。

分步install
真实只警告不拒绝,环境悄悄进入冲突状态
合并install
真实ResolutionImpossible,原子性拒绝
退出码
分步安装成功那一步真实退出码是0

做的事:真实构造一组会冲突的依赖,打成wheel

代码# libc 1.0.0 和 libc 2.0.0 两个版本各打一个wheel # pkga 的 pyproject.toml: dependencies = ["libc<2.0"] # pkgb 的 pyproject.toml: dependencies = ["libc>=2.0"] python3 -m build --wheel --outdir wheelhouse libc1 python3 -m build --wheel --outdir wheelhouse libc2 python3 -m build --wheel --outdir wheelhouse pkga python3 -m build --wheel --outdir wheelhouse pkgb → 真实生成4个wheel文件,libc-1.0.0、libc-2.0.0、pkga-1.0.0、pkgb-1.0.0

验证:分两步pip install,pip真实没有拒绝——它先装后警告

实测 · 本机执行输出
pip install pkga   → 真实装上 libc-1.0.0 + pkga-1.0.0

pip install pkgb   →
  Attempting uninstall: libc
    Found existing installation: libc 1.0.0
    Uninstalling libc-1.0.0:
      Successfully uninstalled libc-1.0.0

  ERROR: pip's dependency resolver does not currently take into account all the
  packages that are installed. This behaviour is the source of the following
  dependency conflicts.
  pkga 1.0.0 requires libc<2.0, but you have libc 2.0.0 which is incompatible.
  Successfully installed libc-2.0.0 pkgb-1.0.0    ← 真实照样"成功"安装了

这是本文认为最容易被误解的真实行为:这条ERROR只是一句诊断信息,不是安装失败——命令的退出码是0,libc-2.0.0和pkgb-1.0.0真实都被装进了环境,libc-1.0.0真实被卸载。这一步执行完,环境里同时装着要求libc<2.0的pkga和真实版本是2.0.0的libc——这是一个真实处于冲突状态、但pip不会阻止你继续用下去的环境,如果pkga的代码里真的用到了libc 1.x才有、2.x已经删掉的API,运行时才会真实报错,而这个报错发生的时间点跟"装错依赖"这个真正原因之间,可能隔了很久。

验证:把两个包放进同一条命令,resolver真实提前拒绝了

实测 · 本机执行输出(全新环境)
pip install pkga pkgb   →

INFO: pip is looking at multiple versions of pkgb to determine which version
is compatible with other requirements. This could take a while.
ERROR: Cannot install pkga==1.0.0 and pkgb==1.0.0 because these package
versions have conflicting dependencies.

The conflict is caused by:
    pkga 1.0.0 depends on libc<2.0
    pkgb 1.0.0 depends on libc>=2.0

ERROR: ResolutionImpossible: ...

之后检查: pip list → 只有pip自己,libc/pkga/pkgb真实一个都没装
这组对比揭示了pip真实的resolver只在"单次调用"范围内做完整的回溯求解(backtracking):pip install pkga pkgb这一条命令里,pip真实会把这次要装的所有requirement放在一起算,算出libc<2.0和libc>=2.0互相矛盾、无解,于是ResolutionImpossible,整个命令原子性地什么都不装。但分成两条独立的pip install pkga和pip install pkgb,第二条命令真实只会去满足pkgb自己声明的依赖(libc>=2.0),它不会回头检查"环境里已经装的pkga是否会因此被破坏"——这正是官方那句警告文案里"does not currently take into account all the packages that are installed"想说的真实含义。真实的教训很直接:分步pip install(常见于Dockerfile里一层一层装、或者交互式一个个试)无法拿到resolver的整体求解保护,想要真实拿到"装不下就整体失败"的安全性,需要把所有直接依赖真实写在同一份requirements.txt或同一条命令里一起交给pip。

分步演示:分步install和合并install的真实差异

每一步都是本文上面用真实打包的wheel跑出来的原始结果。

同一个版本冲突,分步和合并安装的真实结果完全不同
这一步新验证的安装方式

    本文用到的工具

    • 本机 Python 3.14.7,真实的 python -m build / pip install
    • 真实构造的4个互相冲突的本地wheel包(libc 1.0/2.0,pkga/pkgb)
    京ICP备2026067502号-1京公网安备11010502063487号