Python依赖别再装进系统环境:用uv锁定并同步项目

|作者: QUASA 编辑团队|2 分钟阅读
Python依赖别再装进系统环境:用uv锁定并同步项目

要用 uv 建立隔离且可重建的 Python 项目,先执行 uv init,再用 uv add 声明依赖。依赖会安装到项目的 .venv;将 pyproject.toml 和 uv.lock 提交到版本控制后,另一台机器可以用 uv sync 按锁文件重建环境。uv 项目指南列出了这些文件的职责,以及添加依赖和运行项目的命令。

pyproject.toml 记录项目要求,uv.lock 保存解析后的依赖版本,.venv 是本机的安装位置。三者一起解决了“装在哪里”和“下次装什么”这两个问题。PyPA 虚拟环境指南建议把第三方包放入独立环境,避免不同项目的安装互相干扰。

从一个最小项目走完命令

下面用 requests 作为示例直接依赖。开始前确认终端能够执行 uv,并在项目根目录运行后续命令;示例包名可换成项目实际使用的包。

  1. 执行 uv init demo,再执行 cd demo。先查看生成的 pyproject.toml 中的 Python 要求,以及 .python-version 指定的默认解释器版本,确认它们适合准备运行项目的机器。
  2. 执行 uv add requests。uv 将 requests 写入项目依赖,解析它需要的其他包,并更新 uv.lock 和项目环境。后续新增直接依赖也应写入项目声明,而不能只在 .venv 中临时安装。
  3. 执行 uv lock,明确检查并生成或更新锁文件。uv add 本身已经会更新 uv.lock,因此这一步通常不会带来新的版本变化;它适合在审查过依赖声明后单独完成解析。
  4. 执行 uv sync,使 .venv 中安装的包与锁文件对应。再执行 uv run python -c "import requests; print(requests.__version__)",确认项目环境能够导入示例依赖。运行自己的程序时,把这条命令换成 uv run python main.py,并使用实际文件名。

uv run 会在启动命令前检查锁文件和项目环境,通常无需先手动激活 .venv。uv sync 默认执行精确同步,会移除锁文件之外的包;uv run 默认不会清理这些额外包。若某个临时安装的包只有在本机才能导入,它还没有成为可靠的项目依赖。

锁文件、虚拟环境和 requirements.txt 各管什么

虚拟环境隔离安装位置,锁文件约束依赖选择。单独创建 .venv 可以避免一个项目升级包时改动另一个项目的环境,但它没有告诉新机器应选哪些版本。交接项目时应提交声明和锁文件,让目标机器重新安装;.venv 是本机生成的目录,不应作为版本控制中的项目文件。

pyproject.toml 中的依赖声明表达项目主动需要哪些包及其允许的版本范围。uv.lock 则记录解析结果,包括间接依赖的具体版本;uv 管理的这个锁文件可覆盖跨平台项目。不同操作系统和 Python 版本仍可能安装不同的适用文件,因此共用 uv.lock 并不意味着各机器的 .venv 目录逐字节相同。项目若使用可选依赖或依赖组,运行环境还取决于同步时选择了哪些组。

requirements.txt 的作用要看生成方式。手写文件可能只列出直接依赖;pip freeze 生成的文件列出当前环境已安装的包及版本,其中可能混有间接依赖和偶然装入的工具。把后一种清单全部导入 pyproject.toml,会把本来由其他包带来的依赖也变成项目主动维护的依赖,增加日后更新时需要判断的内容。

在另一台机器和 CI 中保持一致

交接时提交 pyproject.toml、uv.lock,以及项目使用的 Python 版本约定,不提交 .venv。新机器取得仓库后,在项目根目录执行 uv sync,再用 uv run 启动程序或测试。若机器上的解释器不满足项目声明的 Python 要求,应准备兼容的解释器;依赖锁文件不能代替解释器版本约定。

CI 可执行 uv sync --locked。它会使用已有锁文件同步环境,并检查锁文件是否仍符合项目声明;如果新增依赖,或修改约束使锁定版本不再满足要求,却没有提交更新后的锁文件,命令会报错。只需单独检查一致性时,可用 uv lock --check。--frozen 则跳过锁文件是否过期的检查,不能代替这一道 CI 检查。

同步后可用 uv run --locked 执行测试或入口命令,使运行前保留同样的锁文件检查。若测试依赖专门的可选依赖或依赖组,本地和 CI 应选择相同的安装选项;共用一份 uv.lock,并不自动保证两边安装了同一组包。

迁移旧项目时,先辨认直接依赖

从 venv 加 requirements.txt 迁移时,先保留旧依赖文件和一份已通过测试的提交。若项目尚无 pyproject.toml,可在项目根目录执行 uv init;若已有该文件,就检查并沿用现有配置。迁移的目标是把项目主动使用的依赖写入声明,再生成 uv.lock,而不是照搬旧虚拟环境。

如果 requirements.txt 本来就是手工维护的直接依赖列表,可执行 uv add -r requirements.txt,并核对 pyproject.toml 中新增的包。如果它由 pip freeze 生成,应先从代码和项目配置中找出直接依赖,再把这些包导入。旧项目若分别保存了未锁定的直接依赖文件 requirements.in 和锁定版本文件 requirements.txt,可执行 uv add -r requirements.in -c requirements.txt,让旧版本作为迁移时的约束;跨平台分别生成的旧文件还需核对平台条件,不能简单地当作一份通用约束。

导入后执行 uv lock、uv sync,并运行原有测试或入口命令。检查直接依赖是否齐全、Python 要求是否合适,以及代码是否暗中依赖旧环境里偶然安装的包。确认新环境可用后,再让团队统一使用 uv 的项目命令。

与 pip 混用时,留意配置和回滚对象

uv 也提供 uv venv 和 uv pip 命令,但 uv pip 与 pip 的行为并非处处相同。uv 的兼容性说明指出,它不读取 pip.conf 或 PIP_INDEX_URL 这类 pip 专用设置;当包同时存在于多个索引时,两者的候选版本选择方式也可能不同。依赖私有索引、预发布版本或特殊安装选项的旧项目,应核对迁移后的 uv 配置和解析结果。

依赖更新后若测试失败,回滚时按项目文件检查,而不是修改系统环境:

  • 查看 pyproject.toml 与 uv.lock 的版本控制差异,区分新增的直接依赖、约束变化和锁定版本变化。
  • 从已验证的提交恢复相互匹配的 pyproject.toml 与 uv.lock;只恢复其中一个文件,可能使声明与锁文件不一致。
  • 执行 uv sync --locked,并运行原有测试。如果 CI 使用了依赖组或可选依赖,也按相同选项重建环境。

相关阅读:

分享:

订阅我们的新闻通讯

将最新 Web3、AI 和加密货币新闻直接发送到您的邮箱。

0