使用uv将依赖项导出到requirements.txt的方法

使用uv将依赖项导出到requirements.txt的方法

2025年11月6日·Jorben
Jorben

在 Python 项目开发中,我们常常面临一个场景:使用现代化的工具进行开发,但需要在标准化的生产环境中部署。 近年来,uv 以其闪电般的速度成为了 Python 项目管理的新宠,极大地提升了依赖安装和虚拟环境管理的效率。

然而,生产环境的构建流程可能仍然依赖于传统的 piprequirements.txt 文件。那么,如何将我们在 uv 下精心管理的依赖项,无缝地迁移到生产环境呢?答案就是 uv pip compile 命令。

核心需求:从开发到生产的依赖导出

假设你的项目使用 pyproject.toml 来声明依赖(这是现代 Python 项目的推荐做法)。开发时,你使用 uv add 来添加依赖,一切都非常顺畅。

但当需要部署时,你可能会遇到以下问题:

  1. 直接导出 pyproject.toml 中的依赖项:这只能得到你直接声明的、版本范围可能很宽的依赖(例如 requests>=2.25.0),这会导致每次安装时都可能得到不同版本的库,不利于环境一致性。
  2. 手动冻结虚拟环境:先进入 uv 创建的虚拟环境,再用 pip freeze > requirements.txt。这种方法可行,但步骤繁琐,且可能包含一些你并不需要的间接依赖(子依赖)。

有没有更优雅、更精确的方法?当然有!

利器登场:uv pip compile

uv pip compile 命令是解决这个问题的瑞士军刀。它的核心作用是进行依赖锁定。它会读取你的 pyproject.toml,解析所有直接和间接依赖,并计算出在当前环境下所有兼容的、精确到最新版本的依赖集合,然后生成一个锁文件。

而我们的目标,正是利用这个功能来生成生产环境可用的 requirements.txt

核心命令详解

让我们来拆解文章开头提到的命令:

uv pip compile pyproject.toml --no-deps -o requirements.txt
  • uv pip compile:这是命令的主体,告诉 uv 要执行依赖编译/锁定操作。
  • pyproject.toml:指定输入的依赖声明文件。uv 会读取其中的 [project][tool.poetry] 等章节下的依赖项。
  • --no-deps:这是关键选项。它告诉 uv只输出在 pyproject.toml 中明确声明的顶级依赖项,但不包括它们的子依赖项。
  • -o requirements.txt-o--output-file 的简写,指定将结果输出到 requirements.txt 文件。

为什么使用 --no-deps

这可能是反直觉的,但非常重要。生产环境依赖管理的黄金法则是:只安装你真正需要的、明确声明的库。

  • 不加 --no-deps:生成的 requirements.txt 会包含所有间接依赖(如 certifiurllib3 等)。虽然能保证一致性,但列表冗长,且你并不直接维护这些库的版本。如果某个子依赖的版本冲突,排查起来会很困难。
  • 加上 --no-deps:生成的 requirements.txt 非常干净,只包含你在 pyproject.toml 中列出的库,并且版本被锁定为当前解析到的最新兼容版本。当使用 pip install -r requirements.txt 时,pip 会自动处理并安装这些顶级依赖所需的子依赖,这更符合生产环境的管理逻辑。

实战演练

假设我们的 pyproject.toml 内容如下:

[project]
name = "my-awesome-app"
version = "0.1.0"
dependencies = [
    "requests>=2.25.0",
    "flask<3.0.0, >=2.0.0",
]

在项目根目录下执行命令:

uv pip compile pyproject.toml --no-deps -o requirements.txt

命令执行后,会生成一个 requirements.txt 文件,其内容可能类似于:

# This file was autogenerated by uv via the following command:
#    uv pip compile pyproject.toml --no-deps -o requirements.txt
blinker==1.7.0
    # via flask
click==8.1.7
    # via flask
flask==2.3.3
itsdangerous==2.1.2
    # via flask
jinja2==3.1.2
    # via flask
markupsafe==2.1.3
    # via jinja2
requests==2.31.0
werkzeug==2.3.7
    # via flask

注意:即使使用了 --no-depsuv 默认仍会以注释形式列出每个依赖项的被引入原因(# via ...),这让文件非常清晰。如果你想要一个极简的、只有包名和版本的文件,可以增加 --no-annotations 选项。

现在,你就可以将这份清晰、精确的 requirements.txt 文件用于生产环境构建了:

pip install -r requirements.txt

进阶用法与技巧

  1. 针对特定平台编译:如果你在 macOS 上开发,但部署到 Linux,可以使用 --platform 选项来确保生成的依赖兼容目标平台。
    uv pip compile pyproject.toml --no-deps --platform linux-x86_64 -o requirements.txt
  2. 生成包含所有依赖的完整文件:如果你的生产环境确实需要一份包含所有子依赖的“冻结”清单(例如为了极致的可重现性),可以省略 --no-deps
    uv pip compile pyproject.toml -o requirements-full.txt
  3. requirements.in 开始:有些项目习惯使用 requirements.in 文件列出顶级依赖,然后编译生成 requirements.txtuv 同样支持:
    uv pip compile requirements.in --no-deps -o requirements.txt

总结

通过 uv pip compile pyproject.toml --no-deps -o requirements.txt 这个命令,我们成功地在 uv 的现代化开发流程和 pip 的传统生产环境部署之间架起了一座桥梁。

这种方法结合了二者的优点:

  • 开发时:享受 uv 带来的高速和便捷。
  • 部署时:生成一份干净、精确、符合生产环境管理哲学的 requirements.txt,确保环境的一致性和可维护性。

希望这篇博文能帮助你更顺畅地管理 Python 项目依赖!如果你还没有尝试过 uv,现在就是一个绝佳的时机。


Last updated on