uv 包构建与发布实战:uv build、uv version 与 uv publish 完整工作流
2026/9/7 10:01:38 网站建设 项目流程

uv 包构建与发布实战:uv build、uv version 与 uv publish 完整工作流

【免费下载链接】uvAn extremely fast Python package and project manager, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/uv/uv

本文围绕 uv 官方指南 Building and publishing a package 展开,系统讲解如何使用uv build将 Python 项目构建为 sdist 与 wheel、如何用uv version以语义化方式更新版本号、以及如何使用uv publish将分发包发布到 PyPI 或自定义索引。文中所有实现细节均对照 uv 仓库源码核实,读完即可在当前仓库或任意 uv 项目中完整复现“构建 → 改版本 → 发布 → 验证安装”的发布流水线。

整体流程

uv 把一个 Python 包从本地代码变成可发布制品的过程拆成了四个命令,每个命令职责单一:

  1. 准备:确认pyproject.toml中声明了[build-system]
  2. 构建uv build在当前项目生成 sdist(.tar.gz)与 wheel(.whl),产物默认写入dist/
  3. 改版本uv version在发布前以精确值或语义化 bump 更新pyproject.toml中的version
  4. 发布uv publishdist/中的分发包上传到包索引,并可附带 PEP 740 构建证明(attestations)。

准备项目

在发布之前,先确保项目具备可打包的构建系统配置。指南给出的关键行为是:

  • 如果项目的pyproject.toml没有[build-system]定义,uv sync等操作不会构建该项目,但uv build回退到传统 setuptools 构建系统继续工作;
  • uv init创建的项目默认包含[build-system]定义,因此开箱即可构建。

官方强烈建议显式配置构建系统,构建系统的完整说明见 项目配置文档。

从源码结构看,这一建议有明确的工程原因:构建前端在发现工作区成员缺少[build-system]时会直接报错,并在错误信息中直接给出可复制的模板(构建前端):

[build-system] requires = ["uv_build>=0.x,<1.0.0"] # 实际运行时按当前 uv 主版本号生成区间 build-backend = "uv_build"

使用uv_build作为构建后端还有额外收益:构建前端会优先走“直接构建”快速路径(下文详述),无需创建 PEP 517 隔离构建环境。

构建包:uv build

最基本的构建命令:

$ uv build

默认情况下,uv build构建当前目录中的项目,并把产物写入dist/子目录。更灵活的用法:

  • uv build <SRC>:构建指定目录中的包,SRC也可以是一个 sdist 归档文件(此时构建出 wheel);
  • uv build --package <PACKAGE>:构建当前工作区中指定的某个包。

指南强调:发布前推荐执行uv build --no-sources。因为uv build默认在解析build-system.requires中的构建依赖时遵循tool.uv.sources配置;而使用pypa/build等其他构建工具时tool.uv.sources是失效的。用--no-sources预演,能确保包在禁用 sources 的环境下依然能正确构建。

完整参数(对照 CLI 源码)

以下参数定义均取自 BuildArgs,可作为日常使用的参数速查:

参数说明
src(位置参数)构建来源:目录,或要转成 wheel 的 sdist 归档;默认当前工作目录
--package <PACKAGE>构建工作区中指定包(工作区从src或当前目录发现),与--all-packages互斥
--all-packages(别名--all构建工作区中所有可构建的包
--out-dir/-o输出目录,默认是源目录(或工作区根目录)下的dist/
--sdist只构建源分发包
--wheel只构建二进制分发包(wheel)
--force-pep517强制走 PEP 517 构建,不使用 uv 构建后端的快速路径
--clear构建前清空输出目录,删除过期产物
--build-constraints/-crequirements.txt风格的约束文件约束构建依赖版本(只约束版本,不会额外引入包)
--no-build-logs隐藏构建后端输出的日志

输出目录的默认逻辑在 构建实现 中:若属于工作区,写入工作区根目录/dist;否则写入源目录/dist;若src是 sdist 文件,则写入该文件所在目录。

底层机制:直接构建与 PEP 517 两条路径

从 build_frontend.rs 的BuildAction判定逻辑可以看到,uv 对每个待构建包会做一次check_direct_build检查:

  • 若构建后端是uv_build且版本兼容,走DirectBuild快速路径——直接调用内置构建后端,跳过 PEP 517 虚拟环境的创建;
  • 否则回退到Pep517路径:创建隔离构建环境(默认开启 build isolation),在其中安装build-system.requires再执行钩子;
  • --force-pep517可以强制走 PEP 517,用于验证包在其他构建工具下的兼容性。

此外,构建用的 Python 解释器按如下顺序发现(源码):① 命令行显式请求;② 项目中的.python-version文件;③pyproject.tomlRequires-Python

更新版本号:uv version

uv version在发布前提供版本更新便利。查看当前版本的方式见 项目指南。

精确设置版本:直接把版本作为位置参数:

$ uv version 1.0.0 hello-world 0.7.0 => 1.0.0

预览而不写入:用--dry-run只打印将发生的变更,不修改pyproject.toml

$ uv version 2.0.0 --dry-run hello-world 1.0.0 => 2.0.0 $ uv version hello-world 1.0.0

语义化递增:用--bump按语义化版本分量递增:

$ uv version --bump minor hello-world 1.2.3 => 1.3.0

--bump支持的分量(来自 VersionBump 枚举):majorminorpatchstablealphabetarcpostdev,典型效果:

bump 分量示例
major1.2.3 => 2.0.0
minor1.2.3 => 1.3.0
patch1.2.3 => 1.2.4
stable1.2.3b4.post5.dev6 => 1.2.3(移除预发布分量,保留 local 分量)
alpha/beta/rc1.2.3b4 => 1.2.3b5
post1.2.3.post5 => 1.2.3.post6
dev1.2.3a4.dev6 => 1.2.3a4.dev7

重复给出多个分量时,按从大到小(majordev)的顺序应用——源码注释明确说明实现会对操作排序后再依次执行(VersionArgs 附近的注释),使用者无需自己考虑顺序。

显式指定分量数值--bump <component>=<value>

$ uv version --bump patch --bump dev=66463664 hello-world 0.0.1 => 0.0.2.dev66463664

稳定版 → 预发布:在预发布分量之外,再 bump 一个 major/minor/patch:

$ uv version --bump patch --bump beta hello-world 1.3.0 => 1.3.1b1 $ uv version --bump major --bump alpha hello-world 1.3.0 => 2.0.0a1

预发布 → 下一个预发布:只需 bump 对应的预发布分量:

$ uv version --bump beta hello-world 1.3.0b1 => 1.3.0b2

预发布 → 稳定版:用stable清除预发布分量:

$ uv version --bump stable hello-world 1.3.1b2 => 1.3.1

指南提示:uv version修改项目后默认会执行 lock 和 sync。用--frozen可跳过 lock 与 sync,用--no-sync只跳过 sync。这两个开关与uv syncuv lock等命令共用同一套语义(见 VersionArgs)。

uv version的实际修改逻辑位于 project_version,它通过uv-pep440BumpCommand计算新版本,并通过PyProjectTomlMut写回pyproject.toml

发布包:uv publish

若需要从 GitHub Actions 发布到 PyPI,完整的 CI 配置见 GitHub 集成指南。

基本命令:

$ uv publish

默认上传dist/目录(CLI 中files参数默认为dist/*,支持 glob),且只选择 wheel、sdist 及它们的 attestations,忽略其他文件(PublishArgs)。

认证方式

方式参数环境变量
Token(推荐)--tokenUV_PUBLISH_TOKEN
用户名--username/-uUV_PUBLISH_USERNAME
密码--password/-pUV_PUBLISH_PASSWORD
keyring--keyring-providerUV_KEYRING_PROVIDER

从 gather_credentials 的文档注释可以看到完整的凭据解析优先级:URL 内嵌的用户名/密码、--token/--username/--password(CLI 覆盖环境变量)、keyring、trusted publishing token,最后才是在终端交互提示输入。

PyPI 注意:PyPI 已不再支持用户名 + 密码发布,必须使用 token。使用 token 等价于--username __token__并把 token 作为密码。这一点在源码中也得到印证:trusted publishing 得到的 token 同样以__token__作为用户名提交(publish.rs)。

Trusted publishing(可信发布)

在 GitHub Actions 等受支持的 CI 环境中使用可信发布时,无需任何凭据——只需在 PyPI 项目上添加 trusted publisher 即可。uv 的行为细节(均有源码佐证):

  • 使用可信发布时,uv 在发布完成后会主动使 PyPI 签发的短时 token 失效(burn),即使发布失败也会尝试,进一步压缩 token 的暴露窗口(burn_trusted_publishing_token 调用);
  • 若失效操作本身失败,uv 只发出警告,不改变发布结果
  • 通过--tokenUV_PUBLISH_TOKEN显式提供的 token不会被吊销(因为那是用户自己的长效凭据)。

自定义索引:publish-url 与 --index

使用[[tool.uv.index]]配置自定义索引时,为其添加publish-url,然后uv publish --index <name>即可。例如:

[[tool.uv.index]] name = "testpypi" url = "https://test.pypi.org/simple/" publish-url = "https://test.pypi.org/legacy/" explicit = true

等价关系(来自 CLI 帮助文本):uv publish --index pypi等价于uv publish --publish-url https://upload.pypi.org/legacy/ --check-url https://pypi.org/simple——即索引的publish-url用于上传,url自动作为查重用的 check URL。

注意:使用uv publish --index <name>时,pyproject.toml必须存在,也就是说发布用的 CI 任务里需要有 checkout 步骤。

未指定--publish-url时,默认上传到 PyPI 的https://upload.pypi.org/legacy/。另外,离线模式下uv publish会直接报错拒绝执行(源码)。

部分失败的恢复:--check-url

uv publish会对失败的上传进行重试,但仍可能中途失败:一部分文件已上传、一部分缺失。处理策略:

  • PyPI:直接重试同一条命令即可,完全相同的文件会被索引忽略;
  • 其他注册表:使用--check-url <index url>(注意传的是索引 URL 而非发布 URL)。使用--index时会自动以索引 URL 作为 check URL。

check 机制的行为(publish.rs 中的查重逻辑):上传前先检查索引,若完全相同的文件已存在则跳过并提示File ... already exists, skipping;上传出错时会再次检查,以处理“同一文件被并行上传两次”的竞争场景。已存在的文件必须与此前上传的逐字节一致,这可以避免同版本下 sdist 与 wheel 内容不一致的错误发布。该索引需支持 SHA-256、SHA-384 或 SHA-512 之一用于比对。

其他实用开关:

  • --dry-run:只本地校验分发元数据,并在提供--check-url/--index时检查索引中已存在的文件,但不上传;
  • files位置参数支持 glob,用于指定非默认目录的分发包。

上传 attestations(构建证明)

注意:部分第三方索引可能不支持 attestations,甚至会拒绝(而非静默忽略)带 attestations 的上传。遇到此类问题时,用--no-attestations或环境变量UV_PUBLISH_NO_ATTESTATIONS关闭默认行为。

另请注意:uv publish目前不生成attestations,attestations 需要在发布前单独创建。

uv publish支持向 PyPI 等支持 PEP 740 的注册表上传 attestations。uv 会自动发现并匹配attestations——给定如下的dist/目录,uv publish会把每个 attestation 与其对应的分发包一起上传:

$ ls dist/ hello_world-1.0.0-py3-none-any.whl hello_world-1.0.0-py3-none-any.whl.publish.attestation hello_world-1.0.0.tar.gz hello_world-1.0.0.tar.gz.publish.attestation

从源码看,文件分组由group_files_for_publishing完成,且上传顺序固定为wheel 在前、sdist 在后,同类型内按文件名排序(publish.rs)。

安装验证

发布后用uv run验证包可安装、可导入:

$ uv run --with <PACKAGE> --no-project -- python -c "import <PACKAGE>"

--no-project用于避免把本地项目目录中的包当作安装来源,确保验证的是索引上的真实制品。

提示:如果包刚发布不久,可能需要加--refresh-package <PACKAGE>,避免命中本地缓存的旧版本。

下一步

  • 构建与发布的更广泛背景,可参考 PyPA 官方的 build-and-publish 指南(uv 文档在“Next steps”中给出的延伸阅读方向);
  • 与 CI/其他软件集成:集成指南索引;
  • GitHub Actions 发布到 PyPI 的完整配置:GitHub 指南。

【免费下载链接】uvAn extremely fast Python package and project manager, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/uv/uv

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询