uv 怎么升级依赖包:单包更新、版本约束、uv.lock 与回滚教程

uv sync 为什么不升级包?用示例讲清 uv lock --upgrade-package、uv add 版本约束、--locked 与 --frozen 的区别,以及依赖更新后的验证和回滚方法。

在 uv 项目中升级一个依赖,先用 uv lock --upgrade-package 包名 更新锁文件,再用 uv sync --locked 安装并验证。若项目声明的版本范围不允许目标版本,需要先调整 pyproject.toml 中的约束。

最常见的误会是把“同步环境”当成“寻找最新版”:运行 uv sync 后包没有变化,往往是 uv 正在遵守已有的锁文件。

本文面向已有 pyproject.toml 的 Python 项目,命令以 PowerShell 为例,核心 uv 命令也适用于 Linux 和 macOS。 示例使用 requests 和 pytest,便于在独立练习目录中验证,不需要连接模型 API。 文档核对日期为 2026 年 10 月 11 日;本文没有测试任何特定 AI 项目升级后的兼容性。

先分清你要更新哪一层

同一句“更新 uv”,可能对应三个不同对象。 先确定对象,可以避免升级了工具却发现项目依赖完全没变。

目标 常用操作 主要影响
uv 程序本身 按原安装渠道升级 uv 包管理工具版本
项目中的一个依赖 uv lock --upgrade-package requests 锁文件中的依赖解析结果
项目允许的依赖范围 uv add "requests>=2.32,<3" 项目声明、锁文件和环境
项目全部依赖 uv lock --upgrade 所有可升级的锁定依赖
通过 uv tool 安装的工具 uv tool upgrade 工具名 工具自己的隔离环境

不要用 uv tool upgrade 更新项目里的 requests。 也不要只升级 uv 程序,就认为 .venv 中的包会自动变新。 工具安装和项目依赖是两个维护范围,详见 uv 工具指南。

pyproject.toml、uv.lock、.venv 分别管什么

可以把一次依赖变更理解成三个连续问题:项目允许什么版本、解析决定用什么版本、当前机器实际装了什么版本。

文件或目录 职责 是否提交到 Git
pyproject.toml 声明直接依赖及允许范围 是
uv.lock 保存精确解析结果 是
.python-version 记录项目选择的 Python 版本 通常是
.venv 当前机器安装出来的环境 否

例如,项目声明如下:

1
2
3
4
5
6
7
[project]
name = "dependency-demo"
version = "0.1.0"
requires-python = ">=3.12,<3.13"
dependencies = [
    "requests>=2.31,<3",
]

这段配置允许 requests 的一段版本范围。 它没有要求每次运行都去安装这段范围内的最新版本。

如果 uv.lock 已锁定一个满足条件的版本,普通同步通常会继续使用它。 这种行为能减少“昨天可以运行,今天重新安装就坏了”的意外。 相关机制见 uv 锁定与同步文档。

用一个独立目录练习单包升级

先确认工具存在:

1
2
uv --version
git --version

选择一个不存在的目录名,避免把练习文件写进正在维护的仓库。 下面使用脚本型项目,不需要配置 Python 包的构建后端。

1
2
3
4
5
6
uv init --bare --python 3.12 uv-update-demo
Set-Location uv-update-demo
uv python pin 3.12
uv add "requests>=2.31,<3"
uv add --dev pytest
uv sync --locked

如果本机没有所需 Python,uv 可能需要下载解释器。 在受限网络中应先解决下载或解释器发现问题,再判断依赖是否存在冲突。 Python 版本选择规则见 官方解释器文档。

创建 main.py:

1
2
3
4
5
6
7
from importlib.metadata import version
import requests

request = requests.Request("GET", "https://example.com/").prepare()
print("requests:", version("requests"))
print("method:", request.method)
print("url:", request.url)

这段代码只构造请求,不发送网络请求。 它能检查包是否可以导入,并显示当前环境真正安装的版本。

1
2
uv run --locked python main.py
uv tree --locked

然后建立练习基线:

1
2
3
4
git init
git status --short
git add pyproject.toml uv.lock .python-version main.py
git commit -m "Record dependency baseline"

提交前确认 .venv 没有进入暂存区。 若尚未配置 Git 身份,可以先保留文件副本;后面的 Git 回滚命令需要已经存在的提交。

升级一个包,并检查它带来的变化

在刚才的项目目录运行:

1
2
3
4
uv lock --upgrade-package requests
git diff -- uv.lock
uv sync --locked
uv run --locked python main.py

这里把解析和安装拆开,是为了先看清锁文件变化,再修改工作环境。 如果更习惯一次完成,也可以使用 uv sync --upgrade-package requests。

“单包升级”表示以这个包为更新目标,并不保证锁文件只改一个条目。 目标包的新版本可能需要不同的间接依赖,解析器仍须找出满足整个项目的组合。

检查差异时重点看这些问题:

  • requests 本身是否变化。
  • 是否增加或删除了间接依赖。
  • 下载源是否仍符合项目预期。
  • 是否有其他约束迫使目标包停留在原版本。

刚创建的练习项目通常已经选中了当前可用版本。 因此再次升级没有差异也属于正常结果,不代表命令失败。

查看安装结果时,使用项目解释器:

1
2
uv run --locked python -c "import sys; print(sys.executable)"
uv run --locked python -c "from importlib.metadata import version; print(version('requests'))"

第一条输出应指向项目环境。 若编辑器显示的版本不同,先检查编辑器选中的 Python 路径。

为什么升级命令仍然停在旧版本

假设项目写的是:

1
2
3
dependencies = [
    "requests==2.31.0",
]

精确约束只允许这个版本。 --upgrade-package 不会替你突破项目声明。

若已完成兼容性评估,决定允许后续版本,可以明确修改范围:

1
2
3
uv add "requests>=2.32,<3"
git diff -- pyproject.toml uv.lock
uv run --locked python main.py

uv add 默认会更新声明、锁文件和环境,因此这一步已经可能安装新包。 请在独立分支中执行,避免把它当成只读查询。

如果只打算验证一个特定版本,应明确声明精确版本,再测试。 不要为了让解析通过而删除全部上限;原来的上限可能记录了项目已知的不兼容边界。

依赖范围还可能受到其他包、Python 版本和平台条件限制。 发生冲突时,应从解析错误中找出互相矛盾的要求。 修改方法和依赖分组见 依赖管理文档。

全量升级适合独立维护窗口

更新整个项目的命令是:

1
2
3
4
uv lock --upgrade
git diff --stat
git diff -- uv.lock
uv sync --locked

全量升级适合依赖维护任务,但不适合混进一个本来只修改页面文字的提交。 变更越大,发现故障后越难判断是哪个包引起的。

建议先更新一个业务依赖并跑验收,再单独处理开发工具。 对于具有原生扩展、GPU 运行库或模型推理后端的项目,还要检查系统库与驱动要求。 锁文件能够记录包解析结果,却不能替代真实设备上的运行验证。

–locked 和 –frozen 怎么选

这两个参数都经常出现在部署命令中,但检查力度不同。

命令 行为 适用情况
uv lock --check 检查锁文件是否与声明一致 提交前检查
uv sync --locked 要求锁文件有效,再同步环境 部署和复现
uv run --locked python main.py 要求锁文件有效,再运行 日常验证
uv sync --frozen 使用现有锁文件,跳过新鲜度检查 已由其他步骤验证锁文件的流程

如果你改了依赖声明,却忘记更新锁文件,--locked 能帮助暴露问题。 用 --frozen 跳过检查,并不能说明这次声明已经生效。

另一个区别发生在环境清理上:uv sync 默认精确同步,会移除锁文件之外的包。 因此手工向 .venv 临时安装的调试包,下一次同步后可能消失。 应保留的开发工具要通过 uv add --dev 写入声明。

升级后用一个小测试检查关键行为

创建 test_main.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import requests


def test_prepare_request_keeps_query_parameter():
    prepared = requests.Request(
        "GET",
        "https://example.com/search",
        params={"q": "hello world"},
    ).prepare()
    assert prepared.method == "GET"
    assert prepared.url == "https://example.com/search?q=hello+world"

执行:

1
2
3
4
uv lock --check
uv run --locked pytest -q
uv run --locked python main.py
git diff --check

这个测试覆盖请求参数编码这一具体行为。 它不能证明网络连接、代理、TLS 或业务接口都正常。 真实项目应补上自己的关键路径,例如文档解析结果、数据库读写或接口响应结构。

如果项目使用 MinerU 解析文档,可以选一份固定 PDF,比较升级前后的页数、表格和图片引用。 把小样本保留为回归输入,比只检查 import 成功更有判断力。

更新失败后怎样恢复

先保留错误日志和差异,确认是否存在本次升级之外的工作。 下面的命令会丢弃这两个文件中的未提交修改,仅适合它们的变化全部属于本次升级时使用。

1
2
3
4
git diff -- pyproject.toml uv.lock
git restore --source=HEAD -- pyproject.toml uv.lock
uv sync --locked
uv run --locked pytest -q

同时恢复声明与锁文件,可以避免两者互相矛盾。 同步后再次运行测试,确认恢复的是可用状态,而不只是文件看起来一样。

如果升级已经提交,应先定位已知可用的提交,再选择相应文件版本或撤销升级提交。 不要直接复制同事的 .venv:环境路径、平台二进制和解释器版本都可能不一致。

依赖回滚也不会自动撤销数据库迁移或程序运行后产生的数据变化。 升级涉及数据格式时,需要应用自己的备份与恢复方案。

只有 requirements.txt 时怎么办

先判断你是想继续维护 requirements 流程,还是正式迁入 uv 项目。 这两个目标不应该在一个升级命令里混着完成。

保留原有流程时,可在单独的虚拟环境中安装:

1
2
uv venv
uv pip install -r requirements.txt

如果 requirements 已经精确固定版本,这条安装命令不会自动解除约束。 要迁入项目工作流,可以在迁移分支初始化项目,再导入依赖:

1
2
3
uv init --bare
uv add -r requirements.txt
uv lock --check

导入一份 pip freeze 导出的文件,可能把间接依赖也变成直接声明。 迁移后应逐项区分应用真正使用的包和其他包带来的依赖。 私有源、可编辑安装和平台条件需要另行核对,详见 官方迁移指南。

给仍使用 pip 的部署流程导出依赖

项目暂时不能全部切换到 uv 时,可以从锁文件导出:

1
uv export --locked --format requirements.txt --no-dev --output-file requirements.txt

该文件应视为锁文件的派生产物。 修改依赖后先更新项目与锁文件,再重新导出,避免同时人工维护两套版本清单。

示例项目没有打包自身;可安装的应用项目还需检查导出文件中是否包含项目本身或本地路径引用。 部署时必须保证对应源码存在,不能假设只复制 requirements 就够了。 导出选项见 官方导出文档。

常见问题快速定位

现象 优先检查 处理方向
sync 后版本没有变化 锁文件已有可用版本 明确执行单包升级
upgrade 后仍是旧版本 精确约束或其他包限制 阅读解析结果,评估后调整声明
终端成功,编辑器导入失败 Python 解释器路径 选择项目 .venv
锁文件正确但安装失败 网络、证书、磁盘、平台 wheel 保留错误信息,按下载或构建阶段排查
部署提示锁文件过期 声明与锁文件是否一起提交 在开发分支更新并验证锁文件
同步后临时工具消失 工具是否写入 dev 依赖 使用 uv add --dev 管理

更新依赖的完成标准是:声明清楚、锁文件一致、安装环境正确,而且关键业务行为通过验证。 确定升级对象后,优先从一个包开始,保留差异和恢复入口。