如何用 pip-tools 一键锁定 Python 依赖?pip-compile 与 pip-sync 完整上手指南
【免费下载链接】pip-toolsA set of tools to keep your pinned Python dependencies fresh.项目地址: https://gitcode.com/gh_mirrors/pi/pip-tools
想象一下:周五下午,你的项目在本机一切正常,代码合并、上线一气呵成。周一早上,部署环境却报出ModuleNotFoundError——同事随手pip install了一个新包,或某个依赖悄悄升级了小版本,应用就这样倒在了"在我电脑上能跑"。这种环境失控的烦恼,正是pip-tools要帮你消除的:它用pip-compile把宽松的依赖声明编译成精确锁定的版本清单,再用pip-sync让环境与清单完全对齐,让你的 Python 依赖既可控又保持新鲜。
pip-tools 本质是两条命令行工具的组合,全项目只有一个核心目标:帮你把"想用的依赖"和"实际装上的依赖"之间的鸿沟填平。接下来,我们从最痛的场景出发,一步步把它用起来。
痛点开场:依赖版本失控,问题都出在"没锁定"
先看一个真实得不能再真实的例子。你的requirements.txt里写着:
django>=4.0半年后,同事把 Django 从 4.0.3 升级到了 4.2,某些 API 行为变了,线上开始告警,但没人知道是谁、什么时候、为什么升级的。更糟的是,本机、测试机、生产机各自装了不同的小版本,三套环境三个样。
问题的根源只有一个:我们只声明了依赖的"下限",却从未锁定它实际的"值"。pip-tools 的思路很直接——把"声明"和"锁定"分成两步:
pip-compile:读取你写好的依赖声明(可以不含版本号),解析出完整依赖树,输出一份每个包都带精确版本号的清单;pip-sync:读取这份清单,把当前环境里多出来的包卸载、缺失的包装上、版本不对的包换掉,最终与环境完全一致。
一句话总结:pip-compile负责"写清楚装什么",pip-sync负责"保证装的就是它"。🔥
三步完成安装:五分钟就能跑起来
安装 pip-tools 和装普通 Python 包没有区别,唯一要注意的是:它必须装在项目的虚拟环境里(原因稍后揭晓)。
第一步,激活虚拟环境:
$ source .venv/bin/activate (venv) $第二步,安装:
(venv) $ python -m pip install pip-tools第三步,验证一下,看到版本号就说明装好了:
(venv) $ pip-compile --version装完你就多了两个命令:pip-compile和pip-sync。如果你习惯用模块方式运行,也可以写python -m piptools compile和python -m piptools sync,效果完全相同。
小提示:如果你在多个 Python 版本间切换,可以用
python3.11 -m piptools compile这类写法,显式指定解释器版本。
五分钟跑通首个示例:从 requirements.in 到锁定的 requirements.txt
现在做第一个实验。新建一个文本文件requirements.in,只写一行、不写版本号:
# requirements.in django运行编译命令:
(venv) $ pip-compile requirements.in几秒后,目录里出现requirements.txt,内容大致长这样(具体版本取决于你当时的 Python 环境):
asgiref==3.6.0 # via django django==4.1.7 # via -r requirements.in sqlparse==0.4.3 # via django注意看两件事:
- 你只声明了
django,但asgiref、sqlparse这些间接依赖也被自动解析并锁定了; - 每行末尾的
# via注释记录了依赖来源,相当于一份"溯源档案",以后排查问题时能一眼看出谁引入了谁。
这就是 pip-tools 的核心价值:你只管声明要什么,它替你算清楚该锁什么。
核心工作流:一次安装、一次配置、一次运行
把上面的实验扩展成日常规范,就是一套三步走的固定动作。
一次配置:在哪里声明依赖
pip-compile 支持多种依赖声明来源,你按项目习惯选一种即可:
- requirements.in 文件:最简单,适合不想把项目打包成 Python 包的情况,我们刚才已经用过了;
- pyproject.toml:新项目的标准做法,
pip-compile会读取[project.dependencies]和[project.optional-dependencies]; - setup.py / setup.cfg:老项目用 setuptools 声明依赖也能被直接识别。
举个例子,一个用 Hatch 打包的 Django 应用,可以这样写pyproject.toml:
[build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] name = "my-cool-django-app" version = "42" dependencies = ["django"] [project.optional-dependencies] dev = ["pytest"]一次运行:编译 + 同步
对着上面的pyproject.toml,一条命令产出生产锁定文件:
(venv) $ pip-compile -o requirements.txt pyproject.toml想连开发依赖一起锁定,加一个--extra参数:
(venv) $ pip-compile --extra dev -o dev-requirements.txt pyproject.toml于是你得到了两份互不干扰的清单:requirements.txt管生产,dev-requirements.txt管开发。
锁定完成之后,轮到pip-sync上场。它会按清单精确校准环境:该装的装、该升级的升级、该卸载的卸载:
(venv) $ pip-sync Uninstalling flake8-2.4.1: Successfully uninstalled flake8-2.4.1 Collecting click==4.1 ... Successfully installed click-4.1从此"部署前跑一遍pip-sync"就和"提交前跑一遍测试"一样自然。🔧
实战案例:三个场景看懂它的用法与收益
光看流程还不够,我们放进三个真实的业务场景里感受一下。
案例一:上线前锁定生产依赖
团队要做一次版本发布,你先写好requirements.in声明所有顶层依赖,然后执行pip-compile生成requirements.txt。把两个文件都提交到版本库:.in是"人类可读的意图",.txt是"机器执行的真相"。
之后任何人 clone 代码,只要pip-sync就能复现出一模一样的环境,生产部署不再依赖"某台机器碰巧装过什么"。收益是实打实的:构建可预测、环境可复现。
案例二:生产与开发依赖分层管理
一个 Django 项目,生产上想用最新的 2.1 版本,开发时还需要 debug 工具。你可以建两个.in文件:
# requirements.in django<2.2# dev-requirements.in -c requirements.txt django-debug-toolbar<2.2关键就在dev-requirements.in第一行的-c requirements.txt——它声明"开发依赖必须受生产锁定文件约束"。先编译生产文件,再编译开发文件:
(venv) $ pip-compile (venv) $ pip-compile dev-requirements.in你会发现即使 Django 2.2 已经发布,开发环境的 Django 仍被约束在 2.1 系列,因为编译时参考了生产清单。最后在开发环境一次同步两份文件:
(venv) $ pip-sync requirements.txt dev-requirements.txt生产与开发既能各取所需,又不会互相污染,这才是分层依赖管理的正确姿势。📌
案例三:定期升级依赖,而不是放任不管
锁定不等于一劳永逸,依赖也需要定期体检。pip-tools 提供了精确的升级控制:
# 只升级 django 到最新版 (venv) $ pip-compile --upgrade-package django # 同时升级多个,并把 requests 固定到 2.0.0 (venv) $ pip-compile --upgrade-package django --upgrade-package requests==2.0.0 # 一次性升级全部依赖 (venv) $ pip-compile --upgrade如果对安全性要求高,还可以开启哈希校验模式:
(venv) $ pip-compile --generate-hashes requirements.in生成的文件里每个包都带--hash=sha256:...,安装时 pip 会逐包校验指纹,杜绝依赖被篡改的风险,CI 环境尤其受用。
常见问题与避坑:新手最容易踩的六个坑
最后把高频坑一次性讲清楚,帮你少走弯路。
锁定之后,
pip-compile不会自动升级。只要现有requirements.txt满足声明,即使有新版本它也不会动。想升级必须显式加--upgrade或--upgrade-package。这不是 bug,而是"稳定优先"的设计。pip-sync不会碰 pip、setuptools 和 pip-tools 自己。它们属于"打包工具",需要升级时请手动执行python -m pip install --upgrade。一定要在虚拟环境里运行。
pip-compile会按当前环境解析依赖的环境标记(比如"仅在 Windows 上安装"这种条件),pip-sync则靠当前环境识别已装包。装错环境,结果就会不对。别手改
requirements.txt。它是由.in或pyproject.toml生成的产物,下次编译会被覆盖。想调整依赖,改声明文件再重新编译。不同环境的编译结果可能不同。操作系统、Python 版本、解释器都会影响依赖解析,跨平台项目最好在每个目标环境分别执行一次
pip-compile。注意 Python 版本要求。pip-tools 需要 Python 3.9 及以上,太老的解释器会直接安装失败。
下一步:把锁定变成日常习惯
到这里,你已经掌握了 pip-tools 最核心的用法:声明依赖、编译锁定、同步环境、按需升级。把它纳入日常流程后,你会发现"依赖又出问题了"这句话在团队里出现的频率直线下降。
想更进一步,项目自带的 docs 目录里还有不少进阶内容可以探索:把pip-compile接进 pre-commit 钩子实现提交前自动校验、用[tool.pip-tools]配置块把常用参数写进pyproject.toml免去重复输入、用--resolver在回溯解析器与旧解析器之间切换……这些技巧能让你的依赖管理流程更自动化。
从今天起,把这两条命令记进小本本:pip-compile管"写清楚",pip-sync管"装得对"。下次再有人抱怨环境不一致,你已经有底气把这个问题彻底解决掉了。⚡
【免费下载链接】pip-toolsA set of tools to keep your pinned Python dependencies fresh.项目地址: https://gitcode.com/gh_mirrors/pi/pip-tools
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考