跳到内容

从 pip 迁移到 uv 项目

本指南将讨论如何将基于 requirements 文件和 pippip-tools 的工作流转换为使用 pyproject.tomluv.lock 文件的 uv 项目工作流。

注意

如果您希望从 pippip-tools 迁移到 uv 的直接替代接口,或者从已经使用 pyproject.toml 的现有工作流迁移,相关指南尚未撰写。请参阅 #5200 以跟踪进展。

我们将从使用 pip 进行开发的总览开始,然后讨论如何迁移到 uv。

提示

如果您熟悉该生态系统,可以直接跳转到 导入需求文件 的说明。

了解 pip 工作流

项目依赖项

当您想在项目中使用某个包时,需要先安装它。pip 支持命令式安装包,例如:

$ pip install fastapi

这会将包安装到 pip 所在的当前环境中。这可能是一个虚拟环境,也可能是您系统 Python 安装的全局环境。

然后,您可以运行需要该包的 Python 脚本:

example.py
import fastapi

为每个项目创建一个虚拟环境是最佳实践,以避免在项目之间混淆包。例如:

$ python -m venv
$ source .venv/bin/activate
$ pip ...

我们将在下方的 项目环境部分 重温这个话题。

需求文件 (Requirements files)

与他人共享项目时,预先声明所需的所有包非常有用。pip 支持从文件安装需求,例如:

requirements.txt
fastapi
$ pip install -r requirements.txt

请注意,上面的 fastapi 没有“锁定”到特定版本——项目中的每个成员安装的 fastapi 版本可能不同。pip-tools 正是为了改善这种体验而创建的。

使用 pip-tools 时,需求文件既指定项目的依赖项,也将依赖项锁定到特定版本——文件扩展名用于区分这两者。例如,如果您需要 fastapipydantic,可以在 requirements.in 文件中指定它们:

requirements.in
fastapi
pydantic>2

请注意 pydantic 上有一个版本约束——这意味着只能使用高于 2.0.0pydantic 版本。相比之下,fastapi 没有版本约束——可以使用任何版本。

这些依赖项可以编译成 requirements.txt 文件:

$ pip-compile requirements.in -o requirements.txt
requirements.txt
annotated-types==0.7.0
    # via pydantic
anyio==4.8.0
    # via starlette
fastapi==0.115.11
    # via -r requirements.in
idna==3.10
    # via anyio
pydantic==2.10.6
    # via
    #   -r requirements.in
    #   fastapi
pydantic-core==2.27.2
    # via pydantic
sniffio==1.3.1
    # via anyio
starlette==0.46.1
    # via fastapi
typing-extensions==4.12.2
    # via
    #   fastapi
    #   pydantic
    #   pydantic-core

在这里,所有的版本约束都是精确的。每个包只能使用单一版本。上述示例是使用 uv pip compile 生成的,也可以使用 pip-tools 中的 pip-compile 生成。

虽然不太常见,但 requirements.txt 也可以通过 pip freeze 生成,即先将输入依赖项安装到环境中,然后导出已安装的版本:

$ pip install -r requirements.in
$ pip freeze > requirements.txt
requirements.txt
annotated-types==0.7.0
anyio==4.8.0
fastapi==0.115.11
idna==3.10
pydantic==2.10.6
pydantic-core==2.27.2
sniffio==1.3.1
starlette==0.46.1
typing-extensions==4.12.2

在将依赖项编译为一组锁定的版本后,这些文件会被提交到版本控制系统并随项目分发。

当有人想要使用该项目时,他们会从需求文件进行安装:

$ pip install -r requirements.txt

开发依赖项

需求文件格式一次只能描述一组依赖项。这意味着如果您有额外的依赖项(例如开发依赖项),则需要单独的文件。例如,我们将创建一个 -dev 依赖文件:

requirements-dev.in
-r requirements.in
-c requirements.txt

pytest

请注意,基础需求通过 -r requirements.in 包含在内。这确保了您的开发环境会考虑到所有依赖项。-c requirements.txt 约束了包版本,以确保 requirements-dev.txt 使用与 requirements.txt 相同的版本。

注意

直接使用 -r requirements.txt 而不是同时使用 -r requirements.in-c requirements.txt 是很常见的做法。生成的包版本没有区别,但使用这两个文件会产生注释,允许您确定哪些依赖项是直接的(带有 -r requirements.in 注释),哪些是间接的(仅带有 -c requirements.txt 注释)。

编译后的开发依赖项如下所示:

requirements-dev.txt
annotated-types==0.7.0
    # via
    #   -c requirements.txt
    #   pydantic
anyio==4.8.0
    # via
    #   -c requirements.txt
    #   starlette
fastapi==0.115.11
    # via
    #   -c requirements.txt
    #   -r requirements.in
idna==3.10
    # via
    #   -c requirements.txt
    #   anyio
iniconfig==2.0.0
    # via pytest
packaging==24.2
    # via pytest
pluggy==1.5.0
    # via pytest
pydantic==2.10.6
    # via
    #   -c requirements.txt
    #   -r requirements.in
    #   fastapi
pydantic-core==2.27.2
    # via
    #   -c requirements.txt
    #   pydantic
pytest==8.3.5
    # via -r requirements-dev.in
sniffio==1.3.1
    # via
    #   -c requirements.txt
    #   anyio
starlette==0.46.1
    # via
    #   -c requirements.txt
    #   fastapi
typing-extensions==4.12.2
    # via
    #   -c requirements.txt
    #   fastapi
    #   pydantic
    #   pydantic-core

与基础依赖文件一样,这些文件会被提交到版本控制系统并随项目分发。当有人想要处理该项目时,他们会从需求文件安装:

$ pip install -r requirements-dev.txt

特定于平台的依赖项

使用 pippip-tools 编译依赖项时,结果只能在生成它的同一平台上使用。这对于需要在多个平台(如 Windows 和 macOS)上使用的项目来说是个问题。

例如,以一个简单的依赖项为例:

requirements.in
tqdm

在 Linux 上,这会被编译为:

requirements-linux.txt
tqdm==4.67.1
    # via -r requirements.in

而在 Windows 上,它会被编译为:

requirements-win.txt
colorama==0.4.6
    # via tqdm
tqdm==4.67.1
    # via -r requirements.in

coloramatqdm 的一个 Windows 专用依赖项。

使用 pippip-tools 时,项目需要为每个支持的平台声明一个需求锁文件。

注意

uv 的解析器可以同时为多个平台编译依赖项(请参阅 “通用解析”),从而允许您为所有平台使用单个 requirements.txt

$ uv pip compile --universal requirements.in
requirements.txt
colorama==0.4.6 ; sys_platform == 'win32'
    # via tqdm
tqdm==4.67.1
    # via -r requirements.in

此解析模式在使用 pyproject.tomluv.lock 时也会被使用。

迁移到 uv 项目

pyproject.toml

pyproject.toml 是 Python 项目元数据的标准化文件。它取代了 requirements.in 文件,允许您表示任意项目依赖组。它还为您的项目元数据(如构建系统或工具设置)提供了集中位置。

例如,上面的 requirements.inrequirements-dev.in 文件可以转换如下的 pyproject.toml

pyproject.toml
[project]
name = "example"
version = "0.0.1"
dependencies = [
    "fastapi",
    "pydantic>2"
]

[dependency-groups]
dev = ["pytest"]

我们将在下面讨论自动化这些导入所需的命令。

uv 锁文件 (lockfile)

uv 使用一个锁文件 (uv.lock) 来锁定包版本。该文件的格式是 uv 特有的,允许 uv 支持高级特性。它取代了 requirements.txt 文件。

当添加依赖项时,锁文件会自动创建和填充,但您也可以使用 uv lock 显式创建它。

requirements.txt 文件不同,uv.lock 文件可以表示任意依赖组,因此不需要多个文件来锁定开发依赖项。

uv 锁文件始终是 通用 的,因此不需要多个文件来 为每个平台锁定依赖项。这确保了所有开发人员在任何机器上都能使用一致、锁定的依赖版本。

uv 锁文件还支持诸如 将包固定到特定索引 等概念,这在 requirements.txt 文件中是无法表示的。

提示

如果您只需要针对部分平台进行锁定,请使用 tool.uv.environments 设置来限制解析和锁文件。

要了解更多信息,请参阅 锁文件 文档。

导入需求文件

首先,如果还没有的话,请创建一个 pyproject.toml

$ uv init

然后,导入需求的最简单方法是使用 uv add

$ uv add -r requirements.in

然而,这个转换过程有一些细微差别。请注意,我们使用了 requirements.in 文件,它不会将包固定到精确版本,因此 uv 会为这些包解析新版本。您可能希望保留之前 requirements.txt 中的锁定版本,以便在切换到 uv 时,您的依赖版本不会发生变化。

解决方法是将您的锁定版本添加为约束。uv 支持在 add 时使用这些约束来保留锁定的版本:

$ uv add -r requirements.in -c requirements.txt

在生成 uv.lock 文件时,您现有的版本将被保留。

导入特定于平台的约束

如果您的特定于平台的依赖项已经编译成单独的文件,您仍然可以过渡到通用锁文件。但是,您不能仅仅使用 -c 来指定现有特定于平台的 requirements.txt 文件中的约束,因为它们不包含描述环境的标记,从而会导致冲突。

要添加必要的标记,请使用 uv pip compile 转换现有文件。例如,给定以下内容:

requirements-win.txt
colorama==0.4.6
    # via tqdm
tqdm==4.67.1
    # via -r requirements.in

标记可以通过以下命令添加:

$ uv pip compile requirements.in -o requirements-win.txt --python-platform windows --no-strip-markers

请注意,结果输出中包含了针对 colorama 的 Windows 标记:

requirements-win.txt
colorama==0.4.6 ; sys_platform == 'win32'
    # via tqdm
tqdm==4.67.1
    # via -r requirements.in

使用 -o 时,如果可以,uv 会将版本约束为与现有的输出文件匹配。

可以通过更改每个需要导入的需求文件的 --python-platform-o 值来为其他平台添加标记,例如,设置为 linuxmacos

一旦每个 requirements.txt 文件都转换完成,就可以使用 uv add 将依赖项导入到 pyproject.tomluv.lock 中:

$ uv add -r requirements.in -c requirements-win.txt -c requirements-linux.txt

导入开发依赖文件

正如在 开发依赖项 部分所讨论的,为开发目的拥有多组依赖项是很常见的。

要导入开发依赖项,请在 uv add 时使用 --dev 标志:

$ uv add --dev -r requirements-dev.in -c requirements-dev.txt

如果 requirements-dev.in 通过 -r 包含了父级 requirements.in,则需要将其剥离,以避免将基础需求添加到 dev 依赖组中。以下示例使用 sed 剥离以 -r 开头的行,然后将结果管道传输给 uv add

$ sed '/^-r /d' requirements-dev.in | uv add --dev -r - -c requirements-dev.txt

除了 dev 依赖组外,uv 还支持任意组名。例如,如果您也有一组专门用于构建文档的依赖项,可以将它们导入到 docs 组中:

$ uv add -r requirements-docs.in -c requirements-docs.txt --group docs

导入依赖来源

当导入本地路径或 Git 仓库上的需求时,例如:

requirements.in
./path-dep
-e ./editable-path-dep
git-dep @ git+https://github.com/astral-sh/git-dep

uv 会将它们映射到 pyproject.toml[tool.uv.sources] 表内的 依赖来源

pyproject.toml
[project]
dependencies = [
    "path-dep",
    "editable-path-dep",
    "git-dep",
]

[tool.uv.sources]
path-dep = { path = "./path-dep" }
editable-path-dep = { path = "./editable-path-dep", editable = true }
git-dep = { git = "https://github.com/astral-sh/git-dep" }

项目环境

pip 不同,uv 不以“活跃”虚拟环境的概念为中心。相反,uv 为每个项目在 .venv 目录中创建一个专用的虚拟环境。该环境是自动管理的,因此当您运行诸如 uv add 之类的命令时,环境会与项目依赖项同步。

在环境中执行命令的首选方法是使用 uv run,例如:

$ uv run pytest

在每次 uv run 调用之前,uv 都会验证锁文件是否与 pyproject.toml 同步,并且环境是否与锁文件同步,从而使您的项目保持同步,无需人工干预。uv run 保证您的命令在一致的、锁定的环境中运行。

项目环境也可以通过 uv sync 显式创建,例如用于编辑器。

注意

在项目中,uv 默认会优先使用项目目录中的 .venv,并忽略由 VIRTUAL_ENV 变量声明的活跃环境。您可以使用 --active 标志选择使用活跃环境。

要了解更多信息,请参阅 项目环境 文档。

下一步

既然您已经迁移到 uv,请查看 项目概念 页面,了解关于 uv 项目的更多详情。