pip install 默认不重试、不降级、不缓存失败,易因网络波动或镜像失效中断;应加 --retries 5 --timeout 60,优先用清华源,拆分下载与安装,用 pip-compile 生成带 hash 的 requirements.txt 防篡改,并在 ci 中用 pip check 检测兼容性。

pip install 时出现 ConnectionError 或 ReadTimeout
网络波动或镜像源失效时,pip install 很可能中断,但默认不重试、不降级、不缓存失败状态,直接报错退出。这不是你环境的问题,是 pip 的默认行为太“刚”。
- 加
--retries 5和--timeout 60是最轻量的补救:重试能绕过瞬时丢包,超时延长可扛住慢速镜像 - 优先换国内可信源,比如清华源:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ requests,别依赖~/.pip/pip.conf全局配置——CI 环境常没这个文件,显式传更稳 - 避免在 Dockerfile 里写
RUN pip install -r requirements.txt这种单点命令:一旦失败整个镜像构建崩掉;拆成下载 + 安装两步,用--find-links file:///wheelhouse --no-index预缓存 wheel 包更可控
requirements.txt 中版本号写死了,但上游包悄悄改了 wheel 内容
写 requests==2.31.0 看似锁死,其实只锁了包名和版本字符串。如果作者删库重推同版本 tag、或在 PyPI 上替换 wheel 文件(虽不推荐但允许),你下次安装拿到的就是被篡改过的二进制。
- 用
pip freeze --all > requirements.txt生成的只是当前环境快照,不含哈希,不能防篡改 - 真正防篡改得用
pip-compile(来自pip-tools)配合requirements.in:它会生成带--hash的requirements.txt,安装时校验每个 wheel 的 sha256 - 注意:带 hash 的文件无法直接
pip install -e .开发安装,开发态建议用pip-sync替代pip install -r,它会严格比对 hash 并拒绝不匹配项
CI 环境里 pip install 总是触发编译,拖慢构建速度
很多 CI 默认用最小化 Python 镜像(如 python:3.11-slim),缺编译工具链,遇到 cffi、cryptography 这类包就现场 gcc 编译,既慢又容易因缺失 libssl-dev 等系统依赖而失败。
- 优先拉预编译 wheel:加
--only-binary=all强制跳过源码编译,失败时再查具体缺哪个系统库 - Docker 构建中,把
apt-get install build-essential libssl-dev libffi-dev python3-dev放在pip install前——但别放太前,否则基础镜像更新后这些包可能冗余或冲突 - 更稳的做法是用
manylinux兼容镜像(如quay.io/pypa/manylinux2014_x86_64)做构建阶段,再 COPY wheel 到 slim 镜像,彻底隔离编译环境
依赖冲突时 pip 自动回退版本,却选了个不兼容的旧版
pip install 在解决依赖冲突时,会尝试降级已装包,但它没有语义化版本(SemVer)感知能力,也不读 setup.py 里的 python_requires,可能把 pydantic>=2.0 降成 1.10.14 —— 而你的代码用了 @field_validator,这功能 2.0 才有。
立即学习“Python免费学习笔记(深入)”;
- 别信 pip 的自动解冲突,用
pip install --dry-run --no-deps先探路,看它打算装什么版本 - 在
pyproject.toml里显式声明[project.requires-python] = ">=3.9"和关键依赖的最小版本(如pydantic = ">=2.5.0"),现代 pip(23.0+)会尊重这个约束 - CI 中跑
pip check作为构建后置步骤:它不解决冲突,但能立刻暴露“requests 2.31.0 is incompatible with urllib3”这类运行时才爆的问题
真正难防的不是网络或版本号,是那些没出现在错误信息里的隐式耦合——比如某个包的 setup.py 里动态 import 了另一模块,而那个模块只在特定 Python 版本下存在。这种问题不会在 pip install 时报错,要等第一次 import 才崩。防御式编程最后一步,永远是让测试覆盖 import 路径。











