为何pip 20.0.2会安装不兼容的idna==2.9版本?
问题背景
我在Ubuntu系统上使用pip 20.0.2通过requirements文件安装项目依赖时,遇到了一个棘手的问题:明明存在满足所有依赖约束的idna 2.8版本,pip却执意安装idna==2.9,而这个版本和直接依赖requests 2.22.0完全不兼容(requests明确要求idna>=2.5,<2.9)。运行pip install -r requirements.txt时直接抛出错误:
ERROR: requests 2.22.0 has requirement idna<2.9,>=2.5, but you'll have idna 2.9 which is incompatible.
通过虚拟环境中执行python -m pipdeptree -r查看依赖树,能清晰看到冲突的根源:
idna==2.9 - cryptography==2.3.1 [requires: idna>=2.1] - requests==2.22.0 [requires: idna>=2.5,<2.9] - requests-oauthlib==1.3.0 [requires: requests>=2.0.0] - social-auth-core==3.2.0 [requires: requests-oauthlib>=0.6.1] - social-auth-app-django==2.1.0 [requires: social-auth-core>=1.2.0] - responses==0.10.9 [requires: requests>=2.0] - social-auth-core==3.2.0 [requires: requests>=2.9.1] - social-auth-app-django==2.1.0 [requires: social-auth-core>=1.2.0]
我的requirements文件结构如下:
requirements.txt
-r requirements/requirements-base.txt -r requirements/requirements-testing.txt
requirements-base.txt
cryptography~=2.3.1 pyjwt~=1.6.4 requests~=2.22.0 social-auth-app-django~=2.1.0
requirements-testing.txt
hypothesis~=3.87.0 pytest~=3.6.2 pytest-django~=3.3.2 pytest-cov~=2.5.1 responses~=0.10.5
甚至我创建了最小复现示例,requirements.txt仅包含:
cryptography~=2.3.1 requests~=2.22.0
执行以下命令后,依然出现同样的错误:
virtualenv -p python3.6 -v venv source venv/bin/activate pip install -r requirements.txt --no-cache-dir
问题原因
这个问题的核心在于pip 20.0.2使用的是旧版本的依赖解析器。旧解析器在处理多依赖约束时逻辑不够完善:它会优先满足某个依赖的最高版本需求(这里cryptography只要求idna>=2.1,所以旧解析器直接选了当时最新的idna 2.9),之后才发现和requests的约束冲突,但此时已经完成安装,只能抛出错误。而pip从20.3版本开始引入了全新的依赖解析器,能够更智能地找出满足所有依赖约束的最优版本。
解决方案
针对这个问题,有几个可行的解决办法:
1. 升级pip到最新版本(推荐)
升级pip到20.3及以上版本,新的解析器会自动处理约束冲突,选择满足所有条件的idna==2.8:
pip install --upgrade pip # 之后重新安装依赖 pip install -r requirements.txt --no-cache-dir
2. 手动指定idna版本
如果因为环境限制无法升级pip,可以直接在requirements文件中强制指定idna==2.8,这样pip会优先安装这个兼容版本:
在requirements-base.txt中添加一行:
idna==2.8
然后重新执行安装命令即可。
3. 更新冲突依赖的版本
如果项目允许,也可以考虑更新requests或cryptography的版本,消除约束冲突:
- 将requests升级到2.23.0及以上版本(这些版本支持
idna>=2.5,<3,兼容idna 2.9) - 或者升级cryptography到更近期的版本(不过需要验证是否会影响其他依赖)
验证
使用方案1升级pip后,重新安装依赖,pip会自动选择idna==2.8,不会再抛出不兼容错误;使用方案2手动指定版本后,安装过程也会顺利完成,所有依赖都能正常运行。
内容的提问来源于stack exchange,提问作者fildred13

