2.6.1 从 pip 迁移到 uv 项目
7 分钟阅读
原文链接: https://docs.astral.sh/uv/guides/migration/pip-to-project/
2.6.1 从 pip 迁移到 uv 项目
本指南讨论如何从以 requirements 文件为中心的 pip 和 pip-tools 工作流,转换为使用 pyproject.toml 和 uv.lock 文件的 uv 项目工作流。
注意
如果你想从
pip和pip-tools迁移到 uv 的即插即用接口,或者从已经在使用pyproject.toml的现有工作流迁移,这些指南尚未编写。可查看 #5200 跟踪进展。
我们先概览使用 pip 的开发方式,然后讨论如何迁移到 uv。
提示
如果你熟悉这个生态,可以直接跳到导入 requirements 文件的说明。
理解 pip 工作流
项目依赖
当你想在项目中使用某个包时,需要先安装它。pip 支持命令式地安装包,例如:
| |
这会把包安装到 pip 所在的环境中。这个环境可能是虚拟环境,也可能是系统 Python 安装的全局环境。
然后,你就可以运行需要该包的 Python 脚本:
| |
最佳实践是为每个项目创建虚拟环境,以避免包之间相互混用。例如:
| |
我们会在下面的项目环境一节中重新讨论这个主题。
requirements 文件
与他人共享项目时,预先声明你需要的所有包很有用。pip 支持从文件安装 requirements,例如:
# requirements.txt
fastapi
| |
注意上面 fastapi 并没有“锁定”到具体版本 —— 参与项目的每个人可能安装了不同版本的 fastapi。pip-tools 正是为了改善这一体验而创建的。
使用 pip-tools 时,requirements 文件既描述项目依赖,也把依赖锁定到具体版本 —— 通过文件扩展名区分这两类文件。例如,如果你需要 fastapi 和 pydantic,可以在 requirements.in 中声明:
# requirements.in
fastapi
pydantic>2
注意 pydantic 上有版本约束 —— 这意味着只能使用高于 2.0.0 的 pydantic 版本。相比之下,fastapi 没有版本约束 —— 任何版本都可以使用。
这些依赖可以被编译到 requirements.txt 文件中:
| |
# requirements.txt
annotated-types==0.7.0
# 来自 pydantic
anyio==4.8.0
# 来自 starlette
fastapi==0.115.11
# 来自 -r requirements.in
idna==3.10
# 来自 anyio
pydantic==2.10.6
# 来自
# -r requirements.in
# fastapi
pydantic-core==2.27.2
# 来自 pydantic
sniffio==1.3.1
# 来自 anyio
starlette==0.46.1
# 来自 fastapi
typing-extensions==4.12.2
# 来自
# fastapi
# pydantic
# pydantic-core
这里所有版本约束都是精确的。每个包只能使用单一版本。上面的示例由 uv pip compile 生成,也可以用 pip-tools 的 pip-compile 生成。
虽然不太常见,requirements.txt 也可以通过 pip freeze 生成:先把输入依赖安装到环境中,再导出已安装的版本:
| |
# 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
把依赖编译成一组锁定的版本之后,这些文件会被提交到版本控制并随项目分发。
之后,当有人想使用该项目时,就从 requirements 文件安装:
| |
开发依赖
requirements 文件格式一次只能描述一组依赖。这意味着如果你有额外的依赖分组(例如开发依赖),就需要单独的文件。例如,我们创建一个 -dev 依赖文件:
# requirements-dev.in
-r requirements.in
-c requirements.txt
pytest
注意基础 requirements 通过 -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
# 来自
# -c requirements.txt
# pydantic
anyio==4.8.0
# 来自
# -c requirements.txt
# starlette
fastapi==0.115.11
# 来自
# -c requirements.txt
# -r requirements.in
idna==3.10
# 来自
# -c requirements.txt
# anyio
iniconfig==2.0.0
# 来自 pytest
packaging==24.2
# 来自 pytest
pluggy==1.5.0
# 来自 pytest
pydantic==2.10.6
# 来自
# -c requirements.txt
# -r requirements.in
# fastapi
pydantic-core==2.27.2
# 来自
# -c requirements.txt
# pydantic
pytest==8.3.5
# 来自 -r requirements-dev.in
sniffio==1.3.1
# 来自
# -c requirements.txt
# anyio
starlette==0.46.1
# 来自
# -c requirements.txt
# fastapi
typing-extensions==4.12.2
# 来自
# -c requirements.txt
# fastapi
# pydantic
# pydantic-core
与基础依赖文件一样,这些文件会被提交到版本控制并随项目分发。当有人想在项目上工作时,就从 requirements 文件安装:
| |
平台相关依赖
用 pip 或 pip-tools 编译依赖时,结果只能在与生成环境相同的平台上使用。这对需要同时支持多个平台(例如 Windows 和 macOS)的项目来说是个问题。
例如,看一个简单的依赖:
# requirements.in
tqdm
在 Linux 上,它编译为:
# requirements-linux.txt
tqdm==4.67.1
# 来自 -r requirements.in
而在 Windows 上,它编译为:
# requirements-win.txt
colorama==0.4.6
# 来自 tqdm
tqdm==4.67.1
# 来自 -r requirements.in
colorama 是 tqdm 仅在 Windows 上需要的依赖。
使用 pip 和 pip-tools 时,项目需要为每个受支持的平台声明一个 requirements 锁文件。
注意
uv 的解析器可以一次性为多个平台编译依赖(参见“通用解析”),让你对所有平台使用同一个
requirements.txt:
1$ uv pip compile --universal requirements.in# requirements.txt colorama==0.4.6 ; sys_platform == 'win32' # 来自 tqdm tqdm==4.67.1 # 来自 -r requirements.in使用
pyproject.toml和uv.lock时也采用这种解析模式。
迁移到 uv 项目
pyproject.toml
pyproject.toml 是 Python 项目元数据的标准文件。它取代 requirements.in 文件,让你可以表示任意分组的项目依赖。它还集中提供项目元数据,例如构建系统或工具设置。
例如,上面的 requirements.in 和 requirements-dev.in 可以转换为如下的 pyproject.toml:
| |
我们会在下面讨论自动完成这些导入所需的命令。
uv 锁文件
uv 使用锁文件(uv.lock)来锁定包版本。该文件的格式是 uv 专有的,使 uv 能够支持高级特性。它取代 requirements.txt 文件。
添加依赖时会自动创建并填充锁文件,你也可以用 uv lock 显式创建它。
与 requirements.txt 不同,uv.lock 文件可以表示任意分组的依赖,因此锁定开发依赖不需要多个文件。
uv 锁文件始终是通用的,所以不需要多个文件来为每个平台锁定依赖。这确保所有开发者无论使用什么机器,都使用一致且锁定的依赖版本。
uv 锁文件还支持诸如把包固定到特定索引的概念,而这是 requirements.txt 无法表达的。
提示
如果你只需要为部分平台锁定,可以使用
tool.uv.environments设置来限制解析和锁文件的范围。
要进一步了解,请参阅锁文件文档。
导入 requirements 文件
首先,如果还没有 pyproject.toml,请先创建:
| |
然后,导入 requirements 最简单的方式是使用 uv add:
| |
不过,这个转换过程有一些微妙之处。注意我们使用的是 requirements.in,它没有把包固定到精确版本,因此 uv 会为这些包求解新版本。你可能希望继续使用 requirements.txt 中此前锁定的版本,这样切换到 uv 后任何依赖版本都不会变化。
解决办法是把已锁定的版本作为约束添加。uv 支持在 add 时使用这些约束来保留锁定版本:
| |
生成 uv.lock 文件时,你现有的版本会被保留。
导入平台相关的约束
如果你的平台相关依赖已被编译到不同文件中,你仍然可以迁移到通用锁文件。但是,不能直接用 -c 指定现有平台相关 requirements.txt 文件中的约束,因为这些文件不包含描述环境的环境标记,因而会产生冲突。
要添加必要的标记,请使用 uv pip compile 转换现有文件。例如,给定:
# requirements-win.txt
colorama==0.4.6
# 来自 tqdm
tqdm==4.67.1
# 来自 -r requirements.in
可以这样添加标记:
| |
注意结果输出在 colorama 上包含了 Windows 标记:
# requirements-win.txt
colorama==0.4.6 ; sys_platform == 'win32'
# 来自 tqdm
tqdm==4.67.1
# 来自 -r requirements.in
使用 -o 时,uv 会尽可能把版本约束到与现有输出文件一致。
要为其他平台添加标记,只需为每个需要导入的 requirements 文件更改 --python-platform 和 -o 的值,例如改为 linux 和 macos。
每个 requirements.txt 文件都转换完成之后,就可以用 uv add 把依赖导入到 pyproject.toml 和 uv.lock:
| |
导入开发依赖文件
正如开发依赖一节所述,为开发目的准备多组依赖很常见。
要导入开发依赖,请在 uv add 时使用 --dev 标志:
| |
如果 requirements-dev.in 通过 -r 包含了父级 requirements.in,则需要把它删掉,以免把基础 requirements 加入 dev 依赖组。下面的示例用 sed 删除以 -r 开头的行,再把结果通过管道传给 uv add:
| |
除了 dev 依赖组之外,uv 还支持任意组名。例如,如果你还有一组专门用于构建文档的依赖,可以把它们导入到 docs 组:
| |
导入依赖来源
导入本地路径或 Git 仓库形式的 requirements 时,例如:
# requirements.in
./path-dep
-e ./editable-path-dep
git-dep @ git+https://github.com/astral-sh/git-dep
uv 会把它们映射为 pyproject.toml 中 [tool.uv.sources] 表里的依赖来源:
| |
项目环境
与 pip 不同,uv 并不以“激活的”虚拟环境这一概念为中心。相反,uv 在 .venv 目录中为每个项目使用专用虚拟环境。该环境会被自动管理,因此当你运行 uv add 这类命令时,环境会与项目依赖保持同步。
在环境中执行命令的首选方式是 uv run,例如:
| |
每次调用 uv run 之前,uv 都会验证锁文件与 pyproject.toml 是否同步、环境与锁文件是否同步,从而让你的项目保持同步,无需人工干预。uv run 保证你的命令运行在一致且已锁定的环境中。
项目环境也可以通过 uv sync 显式创建,例如供编辑器使用。
注意
在项目中,uv 默认优先使用项目目录中的
.venv,并忽略由VIRTUAL_ENV变量声明的激活环境。你可以用--active标志选择使用激活的环境。
要进一步了解,请参阅项目环境文档。
后续步骤
既然你已经迁移到 uv,可以查看项目概念页面,了解更多关于 uv 项目的细节。