首页
/ uv pip compile 深度指南:可复现环境的依赖锁定、升级与同步策略

uv pip compile 深度指南:可复现环境的依赖锁定、升级与同步策略

2026-09-06 11:40:32作者:劳婵绚Shirley

本篇基于 uv 官方文档 docs/pip/compile.md 与对应源码(PipCompileArgspip_compile 等)撰写,完整覆盖 uv pip compile 的锁定用法、升级策略、约束/覆盖文件体系与 uv pip sync 环境同步方案,并结合 Rust 源码说明输出格式推断、既有锁文件偏好复用等底层实现,帮助你在 CI/CD 与多平台交付场景下构建完全可复现的 Python 环境。

1. 为什么需要“锁定”(Locking)

所谓锁定(Locking),就是把一个依赖(例如 ruff)在环境中实际要使用的精确版本写入一个文件。当项目依赖数量多时,锁定精确版本是保证环境可复现的关键:不锁定的话,依赖版本会随时间推移、随不同工具或不同平台而悄悄变化。

uv 允许把依赖锁定为 requirements.txt 格式。官方推荐用标准的 pyproject.toml 声明依赖,但也支持其他依赖声明格式(详见 声明依赖 文档)。

2. 从不同来源锁定依赖

2.1 基础用法:-o 写文件,缺省只打印

$ uv pip compile pyproject.toml -o requirements.txt

注意:默认情况下 uv pip compile 的输出只打印到终端,必须通过 --output-file / -o 参数才会写入文件(该语义在 CLI 参数定义 中明确:只有提供 -o 时才把结果写入指定的 requirements.txtpylock.toml)。

2.2 从 requirements.in 锁定

$ uv pip compile requirements.in -o requirements.txt

2.3 多个来源文件

$ uv pip compile pyproject.toml requirements-dev.in -o requirements-dev.txt

2.4 传统格式:setup.pysetup.cfg

uv 还支持遗留的 setup.pysetup.cfg 格式:

$ uv pip compile setup.py -o requirements.txt

2.5 从标准输入锁定

使用 - 表示从 stdin 读取:

$ echo "ruff" | uv pip compile -

PipCompileArgs 的源码 可以看到,src_file 这一位置参数完整支持的格式为:requirements.txt、带内联元数据的 .py 文件、pylock.tomlpyproject.tomlsetup.pysetup.cfg;当提供 pyproject.tomlsetup.pysetup.cfg 时,uv 会提取对应项目的依赖;当参数为 - 时从 stdin 读取。此外源码还说明了一个容易忽略的细节:多个来源文件及其内部条目的顺序,会用于确定解析(resolution)时的优先级

3. 可选依赖(extras)与依赖组(groups)

3.1 启用指定 extra

$ uv pip compile pyproject.toml --extra foo

3.2 启用全部 extra

$ uv pip compile pyproject.toml --all-extras

注意:requirements.in 格式不支持 extras。 这一点在源码中同样得到了印证:若请求了 extras 但来源不是 pyproject.toml / setup.cfg / setup.py,命令会直接报错 Requesting extras requires a 'pyproject.toml', 'setup.cfg', or 'setup.py' file.(见 compile.rs 的校验逻辑)。此外,对静态可解析的来源,uv 还会校验你请求的每个 extra 是否真实存在,未使用会报 Requested extra(s) not foundcompile.rs)。

3.2 锁定当前项目中的某个依赖组

锁定当前项目目录 pyproject.toml 中定义的依赖组(例如 foo 组):

$ uv pip compile --group foo

重要提示--group 标志是 pip-tools 的 pip compile 所没有的扩展能力(上游仍在讨论中)。uv 预期会与其最终采纳的语法和语义保持兼容。

指定依赖组的来源项目目录:

$ uv pip compile --project some/path/ --group foo --group bar

也可以为每个组直接指定 pyproject.toml 路径:

$ uv pip compile --group some/path/pyproject.toml:foo --group other/pyproject.toml:bar

注意--group 标志不适用于其他已指定的来源。例如 uv pip compile some/path/pyproject.toml --group foo 会从 ./pyproject.toml(而非 some/path/pyproject.toml)读取 foo 组。

CLI 源码 可确认:--group 与位置参数 src_file 同属 sources 参数组(required 组),不提供路径时默认使用工作目录的 pyproject.toml,且可重复提供。

4. 升级已锁定的依赖

当使用输出文件(-o)时,uv 会考虑既有输出文件中已固定的版本:如果某个依赖已被固定,后续的 compile 运行不会升级它。例如:

$ echo "ruff==0.3.0" > requirements.txt
$ echo "ruff" | uv pip compile - -o requirements.txt
# This file was autogenerated by uv via the following command:
#    uv pip compile - -o requirements.txt
ruff==0.3.0

升级单个依赖使用 --upgrade-package

$ uv pip compile - -o requirements.txt --upgrade-package ruff

升级所有依赖则使用 --upgrade 标志。

源码印证compile.rs 中 的“读取锁文件”逻辑清楚地展示了这一机制——若输出文件已存在,uv 会按格式解析它(requirements.txtLockedRequirements::from_preferences(read_requirements_txt(...))pylock.tomlread_pylock_toml_requirements),把其中的固定版本作为**解析偏好(preferences)**传入求解器,同时把其中的 Git 引用(repo + sha)注入 Git 解析缓存,保证后续编译优先复用已锁定的提交。另外,--upgrade / --upgrade-package 会同时隐含 --refresh / --refresh-package 语义(即绕过缓存重新获取该包元数据),见 ResolverArgs 定义

5. 同步环境:uv pip install vs uv pip sync

依赖既可以直接从定义文件安装,也可以从编译好的 requirements.txt 安装,对应 uv pip install(详见 从文件安装包)。

关键区别在于:uv pip install 不会移除环境中已安装但与锁文件冲突之外的包——也就是说环境里可能残留锁文件未声明的依赖,这对可复现性并不理想。要让环境精确匹配锁文件,请使用 uv pip sync

$ uv pip sync requirements.txt

同步 PEP 751 pylock.toml

$ uv pip sync pylock.toml

PipSyncArgs 源码 可以确认,uv pip sync 的输入文件支持 requirements.txtpylock.tomlpyproject.tomlsetup.pysetup.cfg 等格式,且与 compile 同样支持 --extra--all-extras--group 参数,可直接对 pyproject.toml / pylock.toml 的某个依赖组做精确同步。

6. 添加约束(Constraints)

约束文件是 requirements.txt 风格的文件,它只控制被安装依赖的_版本_;但把某个包装进约束文件不会触发该包的安装。约束可以用来给“并非当前项目依赖”的包添加版本边界。

定义约束(例如 constraints.txt):

pydantic<2.0

使用约束文件:

$ uv pip compile requirements.in --constraint constraints.txt

每个文件可定义多个约束,也可以同时使用多个文件(该参数对应源码中的 constraints,别名 --constraint,见 lib.rs)。

uv 还会读取工作区根 pyproject.toml 中的 constraint-dependencies,并追加到约束文件中指定的约束之后(源码中的合并逻辑见 compile.rs:命令行的 constraints 与 constraints_from_workspace 通过 chain 合并)。

7. 添加构建约束(Build Constraints)

--constraint 类似,但专门用于构建时依赖(包括构建运行时依赖所需的构建依赖)。

构建约束文件同样是 requirements.txt 风格,只控制_构建时_需求的版本;把包写进构建约束文件不会触发它在构建时安装——约束只在该包作为直接/传递的构建时依赖被需要时生效。可以用来给未显式声明的构建时依赖添加边界。

例如,某包的构建依赖声明为:

[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

可以用构建约束确保工作区中所有包构建时都使用特定版本的 setuptools

setuptools==75.0.0

对应 CLI 参数为 --build-constraint(别名 --build-constraint/-b 短选项,源码见 lib.rs)。uv 还会读取工作区根 pyproject.toml 中的 build-constraint-dependencies 并追加到构建约束文件之后(合并逻辑见 compile.rs)。

8. 覆盖依赖版本(Overrides)

覆盖文件是 requirements.txt 风格的文件,它会强制安装某个依赖的特定版本——无论任何组成包声明了什么需求,也无论这是否会被视为非法解析。

核心区别在于:约束是_加法_的(additive),与各组成包的需求叠加;覆盖是_绝对_的(absolute),完全替换组成包的需求。

覆盖最常见的用途是移除某个传递依赖的上限。例如:a 要求 c>=1.0,<2.0b 要求 c>=2.0,而当前项目同时依赖 ab,则依赖无法解析。

定义覆盖(例如 overrides.txt):

c>=2.0

使用覆盖文件:

$ uv pip compile requirements.in --override overrides.txt

此时解析可以成功。但请注意:如果 a _正确地_声明了它不支持 c>=2.0,那么在实际使用这些包时很可能遇到运行时错误。每个文件可定义多个覆盖,也可以同时使用多个文件(参数见 lib.rs)。

9. 输出格式与更多实用选项(源码补充)

文档示例之外,PipCompileArgs 还定义了一批与锁定流程直接相关的选项,值得在生产中使用:

选项 作用 说明
-o, --output-file 写入输出文件 若文件已存在,其固定版本会被优先沿用(除非加 --upgrade
--format 输出格式 支持 requirements.txtpylock.toml(PEP 751)两种;未显式指定时按输出文件扩展名推断:.txt → requirements.txt,.toml → pylock.toml,默认 requirements.txt(compile.rs
--generate-hashes 在输出中包含分发哈希 便于配合哈希校验安装
--no-deps 忽略包依赖 只把命令行/文件中显式列出的包写入结果
--universal 通用解析 尝试生成一份兼容所有操作系统、架构与 Python 实现的单一输出文件;--python-version 视为下限;隐含 --no-strip-markers
--python-version 指定解析用的 Python 版本 例如 3.8;省略补丁版本时按最小补丁版本处理(3.83.8.0
--python-platform 指定目标平台 target triple 形式,如 x86_64-unknown-linux-gnu;属于高级用法,注意从源码构建的包按当前平台构建
--no-emit-package 从输出中排除某包 等价于 pip-compile 的 --unsafe-package;该包的依赖仍会保留
--no-strip-extras / --no-strip-markers 保留 extras / 环境标记 默认两者都被剥离,因为单次 compile 只对目标环境保证正确
--no-annotate / --no-header 去掉来源注释 / 文件头 --annotation-style 可控制注释风格(默认 split
--emit-index-url / --emit-find-links / --emit-build-options 把索引、find-links、二进制选项写入输出 默认均不写入
--no-build / --only-binary 控制源码构建 --no-build--only-binary :all: 的别名
--custom-compile-command 自定义头部命令注释 用于包装 uv pip compile 的构建脚本,支持 UV_CUSTOM_COMPILE_COMMAND 环境变量
--exclude / --exclude-newer 排除包 / 限制索引时间 --exclude 指定要完全排除的包文件,--exclude-newer 把索引截断到某个时间点

关于输出文件头:pip_compile 会写入 # This file was autogenerated by uv via the following command: 及实际命令(compile.rs)。cmd 函数 还会刻意从头部命令中剥离 --upgrade--upgrade-package--quiet--verbose 等“非复现性”参数——即文件头记录的是下一次可直接复现该锁文件的命令,而不是带一次性升级参数的原始命令。

另外两个源码中明确的边界值得注意:

  • 输出文件名为 pyproject.toml 会被直接拒绝(pyproject.toml 不是受支持的输出格式,见 compile.rs);
  • 导出为 pylock.toml 时文件名必须符合 pylock.tomlpylock.<name>.toml 规范(compile.rs),且 pylock.toml 是合法输出但不是合法输入(compile.rs)。

10. 推荐工作流小结

结合上述文档与源码行为,一条完整的可复现交付链路是:

  1. 声明:在 pyproject.toml(或 requirements.in / setup.py / setup.cfg)中声明依赖,用 --extra / --all-extras / --group 覆盖需要的可选依赖与依赖组;
  2. 锁定uv pip compile <来源> -o requirements.txt(或 pylock.toml),用 --constraint / --build-constraint / --override 叠加组织级版本边界;
  3. 升级:例行编译保持既有固定版本,需要升级时以 --upgrade-package <pkg> 精准升级或 --upgrade 全量升级;
  4. 同步:用 uv pip sync requirements.txt 让环境精确匹配锁文件,避免 uv pip install 的残留包问题。

相关实现与测试可在仓库中进一步深入:pip compile 命令实现pip sync 命令实现CLI 参数定义pip compile 集成测试锁定的编译结果示例测试需求来源文件

登录后查看全文
热门项目推荐
相关项目推荐