uv 0.5.x 版本全记录:从破坏性变更到 PEP 723 脚本锁文件的技术演进
本文基于 uv 仓库中的 changelogs/0.5.x.md 展开,系统梳理 uv 0.5.0 至 0.5.31 共 32 个版本的演进脉络:10 项破坏性变更的动机与源码实现、0.5.3 引入的“冲突 extras”(conflicting extras)机制及 PyTorch CPU/GPU 双索引实战配置、0.5.17 落地的 PEP 723 脚本锁文件工作流,以及贯穿整个系列的错误信息改进、性能优化与托管 Python 发行版更新。读完后,你将能够理解每次升级需要关注的行为差异,并掌握依赖冲突、Python 版本固定、脚本锁定等高级场景的落地方法。
0.5.x 系列定位:一个“积累型”的版本线
0.5.x 是 uv 发展史上的一个关键阶段。0.5.0 的发布说明中写道:自 Python 版本管理、项目管理和工具管理三大能力上线以来,uv 保持了快速迭代,距离上一次破坏性变更已发布了三十个版本;在此期间积累的大量“提升正确性与用户体验但可能破坏部分工作流”的变更,被集中放入 0.5.0 一并发布。官方也明确表态:大多数用户无需修改任何内容即可升级。
整条 0.5.x 线包含从 0.5.0 到 0.5.31 共 32 个版本,大致可分为四个阶段:
| 阶段 | 版本范围 | 主题 |
|---|---|---|
| 破坏性变更集中发布 + 稳定性打磨 | 0.5.0 – 0.5.2 | 10 项 Breaking Changes、--outdated 系列命令、构建失败诊断增强 |
| 冲突依赖与解析器深化 | 0.5.3 – 0.5.6 | 冲突 extras/依赖组、PubGrub 内部重构、sysconfig 修补 |
| 脚本与工作流完善 | 0.5.7 – 0.5.17 | 构建后端预览特性、uv python 子命令增强、PEP 723 脚本锁文件 |
| 持续修 Bug 与性能优化 | 0.5.18 – 0.5.31 | 托管 Python/PyPy 发行版更新、uvx python、--active/--bare 等 |
0.5.0:10 项破坏性变更逐条解析
0.5.0 的破坏性变更是该系列最核心的内容,每一项都值得结合当前仓库源码理解其落地方式。
1. 用 base executable 设置虚拟环境的 Python 路径
此前 uv 在创建虚拟环境时会先“规范化”(canonicalize)Python 可执行文件的路径,再写入环境。这带来两个问题:会绕过像 Homebrew 这样的稳定版本目录,且与 Python 标准库 venv 模块的行为不一致。0.5.0 起,uv 改用 sys._base_executable 路径。
在当前仓库中可以直接验证这一实现:虚拟环境创建逻辑 中的注释明确写道“为与标准库保持一致,依赖 sys._base_executable,除非使用的是 uv 托管的 Python(此时对符号链接可执行文件可以做得更好)”。具体而言,create 函数会区分两种情况:
// For consistency with the standard library, rely on `sys._base_executable`,
// _unless_ we're using a uv-managed Python (in which case, we can do better
// for symlinked executables).
let base_python = if cfg!(unix) && interpreter.is_standalone() {
interpreter.find_base_python()?
} else {
interpreter.to_base_python()?
};
而 解释器元数据 中获取 base 路径的例程同样依赖 sys._base_executable,在未设置时回退到 sys.executable。这一区分对符号链接密集的发行版(Homebrew、conda 等)尤其重要。
2. 安装器改用 XDG 目录(~/.local/bin)
uv 的安装器此前使用 $CARGO_HOME 或 ~/.cargo/bin 作为目标目录——这与 Cargo 毫无关系,一直是用户抱怨的焦点。0.5.0 起,安装目标按以下优先级确定:
$XDG_BIN_HOME$XDG_DATA_HOME/../bin~/.local/bin
同时,$UV_INSTALL_DIR 环境变量始终可以覆盖目标目录。升级时需要留意的一点是:旧版本安装在 ~/.cargo/bin 中的 uv 不会自动迁移,仓库文档中也相应清理了对 ~/.cargo/bin 的残留引用(0.5.1 的文档更新项)。
3. 向上发现 .python-version 文件
此前 uv 只读取工作目录下的 .python-version 文件;0.5.0 起,uv 会向父目录逐级查找,但不会越过项目边界继续向上搜索。这一行为与 pyenv 和 Rye 保持一致。
当前仓库的 版本文件发现模块 印证了这一设计:PythonVersionFile::discover 从工作目录开始向上寻找“最近”的版本文件,并通过 DiscoveryOptions 中的 stop_discovery_at 字段控制搜索边界——项目边界正是通过该选项传入的。值得注意的是,该模块同时支持 .python-version(单版本固定)和 .python-versions(多版本声明)两种文件名,并可通过 FilePreference 指定优先级。
4. uv.toml 中出现“禁止项”时报错
部分设置只能定义在 pyproject.toml 中。旧行为是:这些设置若出现在 uv.toml 里会被静默忽略;新行为是直接报错。这是一个典型的“宁可报错也不静默”的设计决策——静默忽略会让用户困惑于“为什么我配置了却不生效”。
5. 实现符合 PEP 440 的 local version 语义
这是 0.5.0 中技术含量最高的一项变更。PEP 440 的 local version(如 2.0+cpu)语义在 PubGrub 求解算法中实现极为复杂,uv 此前并不完全符合规范。0.5.0 起,uv 有了规范一致的本地版本实现:对 torch==2.1.0 的请求,即使不带 local tag 的 torch@2.1.0 并不存在,也可以安装 torch@2.1.0+cpu。
这一点在当前仓库的 uv-pep440 版本实现 中可以看到完整支撑:Version 内部维护独立的 local: LocalVersion 字段,支持 Segments(按 + 分隔的段)与 Max(表示大于任意 local 版本)两种形态,并在 with_local / with_local_segments 等方法上提供精确的版本构造与比较能力。
6. Conda base 环境被视为系统环境
旧行为下,uv 不区分 base 与其他 Conda 环境。0.5.0 起,uv 通过 CONDA_DEFAULT_ENV 变量以及 base / default 这两个名字,判断当前经 CONDA_PREFIX 激活的是否为 base 环境;若是,必须使用 --system 标志才能修改它。这是对 Conda 用户误操作 base 环境这一常见事故的防护。
7. != 操作符不再隐式允许预发布版本
此前,版本约束中出现预发布说明符即被视为“opt-in 允许预发布”。新行为下,使用不等于操作符(如 != 2.0a1)时不再允许预发布版本。
其余变更
0.5.0 还有几项范围较窄但同样重要的破坏性变更:
- Windows 主目录选择:从
directoriescrate 切换到etcetera后,优先使用USERPROFILE而非FOLDERID_Profile;USERPROFILE未设置时行为不变。 - 颜色输出的优先级:此前
FORCE_COLOR/NO_COLOR环境变量优先于--color标志;现在显式提供--color时以标志为准。(后续 0.5.15 又补充了对FORCE_COLOR的完整支持。) allow-insecure-host成为全局选项:--allow-insecure-host现在可用于任意命令;为保持一致性,[tool.uv.pip]配置段中的allow-insecure-host设置被移除,统一收敛到[tool.uv]。uv init只为版本不同的 workspace 成员写.python-version:由于 uv 现在会尊重父目录的版本文件,初始化时为成员重复生成同名版本文件已无必要。
0.5.0 的新功能:--outdated 系列与周边增强
0.5.0 同时带来了一批实用性功能:
uv tree --outdated:直接查看依赖树中哪些包有新版可用;pip list --outdated支持(pip 兼容层);- 任意 git 传输协议支持(
git+ssh://、git+http://等); uv cache clean进度条;- armv7l 的 armv8l 别名,支持 arm 32 位兼容模式;
- 直接 URL 之后紧跟分号的解析放宽,以及针对 URL/路径需求中尾部
;的专门报错(这类输入通常是复制粘贴时误带了 marker 分隔符)。
此外,0.5.0 引入了实验性的 uv 构建后端:支持构建基础 source distribution(0.5.0),并在后续版本持续加码——0.5.2 增加源树 → 源码包 → wheel 的转换测试与自定义 glob-walkdir 实现,0.5.6 重构 include/exclude 并加入快速路径,0.5.7 将直接构建接入解析器与安装器、为 uv init 提供 uv 后端模板,0.5.17 补全全部 Python 类型标注。
0.5.3:冲突 extras 与依赖组——PyTorch 双索引实战
0.5.3 是 0.5.x 系列中功能含金量最高的一个版本,核心是解析器对“冲突可选依赖与依赖组”的支持,包括按 extra 或按依赖组粒度指定依赖来源(如 index 分配)。
典型场景是运行时选择 CPU 版还是 GPU 版 PyTorch:在 pyproject.toml 中声明两个互相冲突的 extra,并将它们分别指向不同索引:
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12.0"
[project.optional-dependencies]
# 提供 `--extra cpu` 或 `--extra gpu` 时都包含 `torch`
cpu = ["torch>=2.5.1"]
gpu = ["torch>=2.5.1"]
[tool.uv]
# 允许 `cpu` 与 `gpu` 选择冲突版本的 `torch`
conflicts = [[{ extra = "cpu" }, { extra = "gpu" }]]
[tool.uv.sources]
torch = [
# `--extra cpu` 时从 CPU 专用索引拉取(非 macOS 平台)
{ index = "pytorch-cpu", extra = "cpu", marker = "platform_system != 'Darwin'" },
# `--extra gpu` 时从 GPU 索引拉取
{ index = "pytorch-gpu", extra = "gpu" },
]
[[tool.uv.index]]
name = "pytorch-cpu"
url = "https://download.pytorch.org/whl/cpu"
explicit = true
[[tool.uv.index]]
name = "pytorch-gpu"
url = "https://download.pytorch.org/whl/cu124"
explicit = true
这套机制的要点在于:conflicts 声明让 uv 能够生成通用解析(universal resolution)——uv.lock 同时保留两个 extra 的解,而不是强制在锁定时刻二选一;tool.uv.sources 中的 extra 字段则把“索引分配”绑定到具体的 extra 上;explicit = true 保证这两个索引只服务于被显式指派的包,不会污染默认解析。仓库中 workspace 层对 conflicts 的解析入口 印证了数据来源:conflicts() 方法从 tool.uv.conflicts 读取声明并转换为解析器使用的 Conflicts 结构;源码注释也说明了其用途——“当两个或更多 extras 互相冲突时,显式声明冲突可以让 uv 生成通用解析”。
0.5.3 的其他重点:
--verify-hashes默认开启:pip 兼容层默认执行 hash 校验;uv tool install --force隐含--reinstall-package <name>;- PEP 723 脚本支持 overrides 与 constraints(
[[tool.uv.index]]对脚本的尊重也在本版本修复中落实); - 跨平台启用 Rust 实现的
zlib-rs提升解压性能。
0.5.17:PEP 723 脚本的锁文件
0.5.17 完成了一个完整的工作流闭环:基于 PEP 723 内联元数据为脚本生成锁文件。
行为规则:
- 脚本默认处于“未锁定”状态,需显式执行
uv lock --script /path/to/script.py生成锁文件,位置与脚本相邻(如script.py.lock); - 生成后,
uv run --script、uv add --script、uv remove --script都会尊重并(在必要时)更新该锁文件; uv export --script与uv tree --script新增对脚本的支持,无论脚本是否伴随锁文件均可工作。
当前仓库源码中可以直接找到这一约定:脚本锁目标定位逻辑 中的注释明确以 script.py.lock 作为锁文件命名规则;集成测试 build/audit.rs 也反复用 context.temp_dir.child("script.py.lock") 验证锁文件的创建与 --frozen 下的缺失报错(“Unable to find lockfile at script.py.lock”)。
贯穿 0.5.x 的三条主线
错误信息工程化
整个系列中占比最高的类别是错误与诊断改进,其方向高度一致:把“报错”变成“可操作的诊断”。代表性节点包括:
- 0.5.2:构建失败展示完整推导链(full derivation chain),早期构建失败与安装失败使用 rich 诊断格式;
- 0.5.3:推导链中加入 extra、依赖组与版本约束,
git缺失时给出专门提示; - 0.5.4:空 stdin 的专门警告;
- 0.5.10:大幅精简 PubGrub 求解器错误中的冗余版本枚举,
--no-binary/--no-build失败时给出针对性提示; - 0.5.19:解析器错误中展示期望与实际可用的 ABI tag;
- 0.5.8:
git+前缀缺失时的专门提示。
这些改进大多对应 crates/uv-resolver 与 crates/uv/src/commands 下的错误渲染路径,值得在遇到疑难解析失败时对照查阅。
托管 Python 发行版更新
多个版本携带托管 Python 的批量更新,对使用 uv python install 的用户尤为关键:
| 版本 | 更新内容 |
|---|---|
| 0.5.7 | 新增 CPython 3.9.21、3.10.16、3.11.11、3.12.8、3.13.1 |
| 0.5.15 | Python 3.14.0a3(macOS/Linux)、SQLite 特性检测修复 |
| 0.5.19 | Python 3.14(Windows)、3.14.0a4、64 位 RISC-V Linux 支持 |
| 0.5.29 | CPython 3.12.9、3.13.2,pkg-config 文件可重定位 |
| 0.5.30 | PyPy v7.3.18(PyPy3.10 / PyPy3.11 beta) |
| 0.5.31 | CPython 3.14.0a5(含尾调用解释器)、OpenSSL 3.0.15 → 3.0.16 安全更新 |
配套能力也在同步演进:0.5.2 允许在 uv.toml 配置 Python/PyPy 下载镜像,0.5.8 新增 --install-dir、--show-urls、--only-downloads、--all-arches 等 uv python 选项,0.5.6 起(预览特性)支持 uv python install --default。
性能与内部重构
性能优化条目数量庞大,反映出 0.5.x 期间对解析器热路径的持续雕琢:
- 数据结构瘦身:
Dist从 352 字节缩到 288 字节(0.5.16),进一步降到 200 字节(0.5.19);WheelFilename缩至 48 字节; - 内存与分配:包名/extra/group 名改用
ArcStr,PEP 508 解析器免分配(0.5.17),标记值免String克隆(0.5.6); - 求解器:迁移到 PubGrub arena 管理包名(0.5.5)、0.5.17 将非 first-match 索引策略的抓取并发化、0.5.22 起通过 GitHub API 快速路径直接取
pyproject.toml(并配套校验与非致命回退)、0.5.27 升级 PubGrub 的集基过时优先级跟踪; - 内存分配器:0.5.14 让 jemalloc 真正作为替代分配器生效,0.5.16 全平台重新启用
zlib-ng(s390x/PowerPC/FreeBSD 除外)。
后续版本速览(0.5.21 – 0.5.31)
- 0.5.21:新增
UV_VENV_SEED环境变量(控制是否向新建虚拟环境预装setuptools/pip等种子包); - 0.5.23:
uv venv --refresh、--no-default-groups命令行标志; - 0.5.25:首次发布 Windows aarch64 二进制,发布签名采用 GitHub attestations(0.5.27 完善);
- 0.5.26:
uvx python支持——可直接以工具方式运行任意版本 Python; - 0.5.29:
uv init --bare(仅生成最小pyproject.toml);--active让项目命令尊重已激活的VIRTUAL_ENV,--no-active可静默相关警告; - 0.5.30:
uv sync --dry-run,NO_BINARY/NO_BINARY_PACKAGE环境变量; - 0.5.31:
uv sync --script补全脚本工作流,工具请求支持完整 PEP 508 语法,uv run获得无限递归检测。
升级指引
结合 0.5.x 的整体走向,升级时的检查清单:
- 确认 uv 安装位置:若仍在使用
~/.cargo/bin中的旧安装,0.5.0 起安装器默认指向~/.local/bin(XDG 目录),必要时用UV_INSTALL_DIR显式指定; - 检查
uv.toml:其中若存在仅允许出现在pyproject.toml的设置,新版会直接报错(0.5.0);[tool.uv.pip]下的allow-insecure-host需迁移到[tool.uv]; - 符号链接密集的 Python 安装(Homebrew、conda base):虚拟环境内的解释器路径解析行为已变化,Conda base 环境现在需要
--system才能修改; - 依赖预发布的声明:依赖
!= x.yaN形式的“隐式预发布 opt-in”的解析结果可能变化; - local version 依赖:如依赖
2.0+cpu这类 local 版本,0.5.0 起规范语义下解析结果可能与旧版不同(更正确,但结果可能变化)。
除上述各点外,官方预期大多数工作流无需改动即可平滑升级。更细粒度的逐版本条目可查阅 changelogs/0.5.x.md 原文,与 changelogs/0.4.x.md、changelogs/0.6.x.md 衔接阅读可还原 0.4 → 0.6 的完整演进;本文涉及的源码证据集中在 crates/uv-virtualenv/src/virtualenv.rs、crates/uv-python/src/version_files.rs、crates/uv-pep440/src/version.rs、crates/uv-workspace/src/pyproject.rs 与 crates/uv/src/commands/project/lock_target.rs,可按图索骥进一步深入。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00