从 pip 迁移到 uv 项目
本指南将讨论如何将基于 requirements 文件和 pip 及 pip-tools 的工作流转换为使用 pyproject.toml 和 uv.lock 文件的 uv 项目工作流。
注意
如果您希望从 pip 和 pip-tools 迁移到 uv 的直接替代接口,或者从已经使用 pyproject.toml 的现有工作流迁移,相关指南尚未撰写。请参阅 #5200 以跟踪进展。
我们将从使用 pip 进行开发的总览开始,然后讨论如何迁移到 uv。
提示
如果您熟悉该生态系统,可以直接跳转到 导入需求文件 的说明。
了解 pip 工作流
项目依赖项
当您想在项目中使用某个包时,需要先安装它。pip 支持命令式安装包,例如:
这会将包安装到 pip 所在的当前环境中。这可能是一个虚拟环境,也可能是您系统 Python 安装的全局环境。
然后,您可以运行需要该包的 Python 脚本:
为每个项目创建一个虚拟环境是最佳实践,以避免在项目之间混淆包。例如:
我们将在下方的 项目环境部分 重温这个话题。
需求文件 (Requirements files)
与他人共享项目时,预先声明所需的所有包非常有用。pip 支持从文件安装需求,例如:
请注意,上面的 fastapi 没有“锁定”到特定版本——项目中的每个成员安装的 fastapi 版本可能不同。pip-tools 正是为了改善这种体验而创建的。
使用 pip-tools 时,需求文件既指定项目的依赖项,也将依赖项锁定到特定版本——文件扩展名用于区分这两者。例如,如果您需要 fastapi 和 pydantic,可以在 requirements.in 文件中指定它们:
请注意 pydantic 上有一个版本约束——这意味着只能使用高于 2.0.0 的 pydantic 版本。相比之下,fastapi 没有版本约束——可以使用任何版本。
这些依赖项可以编译成 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 生成,即先将输入依赖项安装到环境中,然后导出已安装的版本:
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
在将依赖项编译为一组锁定的版本后,这些文件会被提交到版本控制系统并随项目分发。
当有人想要使用该项目时,他们会从需求文件进行安装:
开发依赖项
需求文件格式一次只能描述一组依赖项。这意味着如果您有额外的依赖项组(例如开发依赖项),则需要单独的文件。例如,我们将创建一个 -dev 依赖文件:
请注意,基础需求通过 -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 注释)。
编译后的开发依赖项如下所示:
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 或 pip-tools 编译依赖项时,结果只能在生成它的同一平台上使用。这对于需要在多个平台(如 Windows 和 macOS)上使用的项目来说是个问题。
例如,以一个简单的依赖项为例:
在 Linux 上,这会被编译为:
而在 Windows 上,它会被编译为:
colorama 是 tqdm 的一个 Windows 专用依赖项。
使用 pip 和 pip-tools 时,项目需要为每个支持的平台声明一个需求锁文件。
注意
uv 的解析器可以同时为多个平台编译依赖项(请参阅 “通用解析”),从而允许您为所有平台使用单个 requirements.txt:
colorama==0.4.6 ; sys_platform == 'win32'
# via tqdm
tqdm==4.67.1
# via -r requirements.in
此解析模式在使用 pyproject.toml 和 uv.lock 时也会被使用。
迁移到 uv 项目
pyproject.toml
pyproject.toml 是 Python 项目元数据的标准化文件。它取代了 requirements.in 文件,允许您表示任意项目依赖组。它还为您的项目元数据(如构建系统或工具设置)提供了集中位置。
例如,上面的 requirements.in 和 requirements-dev.in 文件可以转换如下的 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 add:
然而,这个转换过程有一些细微差别。请注意,我们使用了 requirements.in 文件,它不会将包固定到精确版本,因此 uv 会为这些包解析新版本。您可能希望保留之前 requirements.txt 中的锁定版本,以便在切换到 uv 时,您的依赖版本不会发生变化。
解决方法是将您的锁定版本添加为约束。uv 支持在 add 时使用这些约束来保留锁定的版本:
在生成 uv.lock 文件时,您现有的版本将被保留。
导入特定于平台的约束
如果您的特定于平台的依赖项已经编译成单独的文件,您仍然可以过渡到通用锁文件。但是,您不能仅仅使用 -c 来指定现有特定于平台的 requirements.txt 文件中的约束,因为它们不包含描述环境的标记,从而会导致冲突。
要添加必要的标记,请使用 uv pip compile 转换现有文件。例如,给定以下内容:
标记可以通过以下命令添加:
$ uv pip compile requirements.in -o requirements-win.txt --python-platform windows --no-strip-markers
请注意,结果输出中包含了针对 colorama 的 Windows 标记:
colorama==0.4.6 ; sys_platform == 'win32'
# via tqdm
tqdm==4.67.1
# via -r requirements.in
使用 -o 时,如果可以,uv 会将版本约束为与现有的输出文件匹配。
可以通过更改每个需要导入的需求文件的 --python-platform 和 -o 值来为其他平台添加标记,例如,设置为 linux 和 macos。
一旦每个 requirements.txt 文件都转换完成,就可以使用 uv add 将依赖项导入到 pyproject.toml 和 uv.lock 中:
导入开发依赖文件
正如在 开发依赖项 部分所讨论的,为开发目的拥有多组依赖项是很常见的。
要导入开发依赖项,请在 uv add 时使用 --dev 标志:
如果 requirements-dev.in 通过 -r 包含了父级 requirements.in,则需要将其剥离,以避免将基础需求添加到 dev 依赖组中。以下示例使用 sed 剥离以 -r 开头的行,然后将结果管道传输给 uv add:
除了 dev 依赖组外,uv 还支持任意组名。例如,如果您也有一组专门用于构建文档的依赖项,可以将它们导入到 docs 组中:
导入依赖来源
当导入本地路径或 Git 仓库上的需求时,例如:
uv 会将它们映射到 pyproject.toml 中 [tool.uv.sources] 表内的 依赖来源。
[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 调用之前,uv 都会验证锁文件是否与 pyproject.toml 同步,并且环境是否与锁文件同步,从而使您的项目保持同步,无需人工干预。uv run 保证您的命令在一致的、锁定的环境中运行。
项目环境也可以通过 uv sync 显式创建,例如用于编辑器。
注意
在项目中,uv 默认会优先使用项目目录中的 .venv,并忽略由 VIRTUAL_ENV 变量声明的活跃环境。您可以使用 --active 标志选择使用活跃环境。
要了解更多信息,请参阅 项目环境 文档。
下一步
既然您已经迁移到 uv,请查看 项目概念 页面,了解关于 uv 项目的更多详情。