当前位置:首页>python>Python wheel 文件名称规范,以及对应的构建选项

Python wheel 文件名称规范,以及对应的构建选项

  • 2026-09-07 14:41:05
Python wheel 文件名称规范,以及对应的构建选项

很多人把生成 .whl 文件称为“编译 whl”。更准确地说,wheel 是 Python 的二进制分发格式:纯 Python 项目主要是打包,包含 C、C++、Rust 等原生扩展的项目才会在构建时编译二进制代码。

判断一个 wheel 能不能安装,最有用的线索往往就是它的文件名。下面从这个文件开始:

my_demo_package-1.2.0-1-cp312-abi3-manylinux_2_17_x86_64.whl

按照 wheel 规范,它的基本格式是:

{distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl

其中 build tag 是可选的,所以常见的纯 Python wheel 可能是:

my_demo_package-1.2.0-py3-none-any.whl

本文只围绕这些字段展开:每一段表示什么、pip 如何使用它、构建时可以通过什么方式影响它,以及哪些事情不能靠改文件名解决。

一、先记住一个完整例子

my_demo_package-1.2.0-1-cp312-abi3-manylinux_2_17_x86_64.whl│                │     │ │      │     └─ platform tag│                │     │ │      └─────── abi tag│                │     │ └────────────── python tag│                │     └──────────────── build tag│                └────────────────────── version└────────────────────────────────────── distribution

可以把它理解成一份“安装筛选信息”:

  • distribution
     告诉 pip 这是哪个发行项目;
  • version
     告诉 pip 这是哪个版本;
  • build tag
     区分同一项目、同一版本下的多个构建产物;
  • python tag
     描述 Python 实现和版本;
  • abi tag
     描述二进制接口兼容性;
  • platform tag
     描述操作系统、架构和系统 ABI。

pip 会把这些标签和当前解释器支持的标签进行匹配。匹配不上时,即使文件确实存在,pip 也不会选择它。

二、distribution:项目名,不是 import 名

看下面的配置:

[project]name = "my-demo-package"version = "1.2.0"

构建后通常得到:

my_demo_package-1.2.0-py3-none-any.whl

这里有三个容易混淆的名字:

发行项目名(distribution):my-demo-packagewheel 文件名中的项目名:   my_demo_packagePython 导入名(import):   my_demo_package

发行项目名用于 pip install:

python -m pip install my-demo-package

导入名用于 Python 代码:

import my_demo_package

它们不要求相同。

项目名为什么变成下划线

wheel 文件名中的 distribution 字段不能直接保留任意分隔符。根据 PEP 427,项目名中的一段或多段 -、_、. 会在 wheel 文件名中转义为一个 _。

因此,下面这些项目名在 wheel 文件名中通常都会变成同一个形式:

my-demo-packagemy_demo_packagemy.demo.package

结果通常是:

my_demo_package-1.2.0-py3-none-any.whl

这和 PEP 440 的版本号规范化不是一回事。PEP 440 管版本号;这里讨论的是项目名字段和 wheel 文件名转义。

能不能让项目名部分保留中划线

不能把标准 wheel 的 distribution 字段强行写成:

my-demo-package-1.2.0-py3-none-any.whl

wheel 文件名使用中划线分隔字段。项目名部分再包含中划线时,解析器无法可靠区分项目名和后面的版本、标签。因此,正常构建会把项目名中的中划线转成下划线。

不要构建后直接执行:

mv my_demo_package-1.2.0-py3-none-any.whl \   my-demo-package-1.2.0-py3-none-any.whl

这样既没有修改 wheel 内部的 .dist-info 元数据,也可能导致 pip 报 Invalid wheel filename。如果业务系统要求展示带中划线的名称,可以把它放在制品库目录、下载链接或外层归档名中,wheel 文件本身保持标准名称。

和项目名有关的构建选项

现代项目在 pyproject.toml 中设置:

[project]name = "my-demo-package"

然后构建:

python -m build --wheel

--wheel 只控制构建 wheel,不会改变项目名的转义规则。setuptools、Hatchling、Flit 等后端都应遵守 wheel 文件名规范。

三、version:版本号,受 PEP 440 约束

示例中的:

my_demo_package-1.2.0-py3-none-any.whl                 └───── version

版本号来自项目元数据:

[project]version = "1.2.0"

建议使用 PEP 440 版本,例如:

1.2.01.2.0rc11.2.0.post11.2.0.dev31.2.0+cuda12

这里才是 PEP 440 发挥作用的地方。不要把“版本号规范化”和“项目名在 wheel 文件名中转义为下划线”混为一谈。关于版本号的规范,我将在下一篇文章详细介绍。

和版本有关的构建配置

最推荐在 pyproject.toml 中声明固定版本:

[project]version = "1.2.0"

如果版本来自 Git 标签或 CI,一般使用构建后端的动态版本配置,例如 setuptools-scm:

[project]dynamic = ["version"][tool.setuptools_scm]

具体配置取决于后端,不建议在构建完成后手改 wheel 名称。

若要表示 CUDA、CPU、客户版等差异,可以考虑本地版本:

my_demo_package-1.2.0+cuda12-py3-none-any.whl

但本地版本是否能发布到目标仓库、如何参与版本比较,要遵守 PEP 440 和仓库策略。另一种方式是使用不同的发行项目名,例如 my-demo-package-cuda,或把变体放到制品库目录中。

四、build tag:同一版本的构建编号

带 build tag 的示例:

my_demo_package-1.2.0-1-cp312-abi3-manylinux_2_17_x86_64.whl                      └─ build tag

它用于区分同一项目、同一版本和同一兼容标签下的不同构建结果。例如修复了构建脚本、重新打包但没有改变项目版本时,可以增加构建编号。

PEP 427 要求 build tag 以数字开头,所以 1、1custom 这类形式才符合要求;随意写成 cuda 不合规。

和 build tag 有关的构建选项

遗留的 setuptools/wheel 命令可以这样写:

python setup.py bdist_wheel --build-number 1

现代项目不建议把 setup.py 当作主要入口,应优先使用:

python -m build --wheel

至于 build tag 是否能通过 pyproject.toml 设置,没有跨构建后端统一的标准配置;如果后端没有提供对应选项,就不要自行拼接文件名。

build tag 不是平台标签,也不是 CUDA 标签。下面这种写法不要使用:

my_demo_package-1.2.0-py3-none-any-cuda.whl

它破坏了 wheel 文件名的字段结构。

五、python tag:支持哪个 Python 实现

示例中的:

my_demo_package-1.2.0-1-cp312-abi3-manylinux_2_17_x86_64.whl                        └───── python tag

常见值包括:

标签
含义
py3
兼容 Python 3 的纯 Python 代码
py39
面向 Python 3.9 的纯 Python 代码
cp312
CPython 3.12
pp39
面向相应 Python 版本的 PyPy 实现

纯 Python 包常见的是:

py3-none-any

如果 wheel 里有 CPython 原生扩展,通常会出现 cp311、cp312 等标签。

和 python tag 有关的构建选项

旧式 bdist_wheel 支持:

python setup.py bdist_wheel --python-tag py3

但这个参数只是声明标签,不会把只支持 CPython 3.12 的二进制扩展变成通用 py3。标签必须和实际代码兼容性一致。

现代构建通常由后端根据项目内容自动生成:

python -m build --wheel

需要构建多个 Python 版本时,应该在多个 Python 环境或 CI 矩阵中分别构建,或者使用 cibuildwheel:

python -m cibuildwheel --output-dir wheelhouse

不要在构建完成后仅修改 cp312、cp311 等文本。改名不会重新编译扩展。

六、abi tag:二进制接口是否兼容

示例中的:

my_demo_package-1.2.0-1-cp312-abi3-manylinux_2_17_x86_64.whl                              └──── abi tag

常见值包括:

标签
含义
none
不依赖特定 Python ABI,纯 Python wheel 常见
cp312
依赖 CPython 3.12 ABI
abi3
使用 CPython 稳定 ABI,可覆盖多个 CPython 版本

例如:

my_demo_package-1.2.0-cp312-cp312-manylinux_2_17_x86_64.whlmy_demo_package-1.2.0-cp39-abi3-manylinux_2_17_x86_64.whl

第一种通常只适合 CPython 3.12;第二种表示以 CPython 3.9 为最低 Python 标签并使用稳定 ABI,实际兼容范围还取决于扩展代码和平台标签。

和 abi tag 有关的构建配置

ABI 不是一个应该随意填写的字符串。它通常由扩展构建工具根据编译结果决定。若使用 setuptools 构建 CPython 稳定 ABI,需要在扩展配置中启用 limited API,并使用与后端匹配的配置方式,例如部分项目会设置 py_limited_api。

关键原则是:

编译选项和扩展代码决定 ABI,文件名只是对结果的声明。

--config-setting 可以向 PEP 517 构建后端传递配置:

python -m build --wheel -Ckey=valuepython -m pip wheel . --config-settings key=value

但 key=value 的含义由具体后端决定,不存在一个对所有项目通用的“设置 abi3”参数。改文件名中的 none 或 abi3 不会改变二进制接口。

七、platform tag:支持哪个操作系统和架构

示例中的最后一段:

my_demo_package-1.2.0-1-cp312-abi3-manylinux_2_17_x86_64.whl                                   └──────────────────── platform tag

常见值包括:

标签
含义
any
不依赖平台,纯 Python wheel 常见
win_amd64
Windows 64 位 x86
macosx_11_0_arm64
macOS 11 及以上 ARM64
manylinux_2_17_x86_64
满足 manylinux glibc 2.17 兼容要求的 Linux x86_64
musllinux_1_2_x86_64
面向 musl libc 的 Linux x86_64

纯 Python 项目通常生成 py3-none-any,含原生扩展的项目则会带操作系统和架构信息。

和 platform tag 有关的构建选项

旧式 wheel 命令支持:

python setup.py bdist_wheel \  --plat-name manylinux_2_17_x86_64

这个选项只是为构建结果指定平台标签。它不会把 Windows 二进制变成 Linux 二进制,也不会自动满足 manylinux 的系统库约束。

更可靠的做法是在目标平台或标准构建容器中构建,使用 cibuildwheel 生成多平台产物:

python -m cibuildwheel --output-dir wheelhouse

auditwheel、delocate 等工具可以帮助检查或修复部分平台依赖,但它们同样不能替代正确的编译环境。

下面这种改名是错误的:

mv package-1.0.0-cp312-cp312-linux_x86_64.whl \   package-1.0.0-cp312-cp312-win_amd64.whl

文件内的动态库格式、系统调用和依赖没有变化。pip 可能因此在 Windows 上错误地选择这个文件,最终安装或导入失败。

八、文件名里的中划线和下划线到底怎么理解

wheel 文件名中的中划线主要是字段分隔符:

distribution-version-build-python-abi-platform.whl

所以文件名中一定会出现中划线。它不是项目名中划线的保留方式。

下划线常见于两个地方:

  1. distribution 字段:项目名中的 -、_、. 按 wheel 规则转义为 _;
  2. platform tag:例如 win_amd64、manylinux_2_17_x86_64,下划线用于平台标签内部的结构。

因此,下面这个文件名里的下划线含义并不相同:

my_demo_package-1.2.0-py3-none-manylinux_2_17_x86_64.whl└── 项目名转义                         └── 平台标签内部组成

九、从文件名反推构建和排错

pip 为什么不选择某个 wheel

先看当前解释器支持的标签:

python -m pip debug --verbose

再对照文件名中的 python tag、abi tag、platform tag。例如当前是 CPython 3.11,而目录里只有 cp312-cp312,pip 不选它是正常行为。

如何一次构建多个平台

不要复制一个 wheel 然后修改最后一段。使用构建矩阵或 cibuildwheel:

python -m cibuildwheel --output-dir wheelhouse

最终应该得到多个真实构建的文件,例如:

demo-1.0.0-cp311-cp311-manylinux_2_17_x86_64.whldemo-1.0.0-cp311-cp311-win_amd64.whldemo-1.0.0-cp311-cp311-macosx_11_0_arm64.whl

如何指定输出目录

这不会改变文件名字段,只改变产物位置:

python -m build --wheel --outdir wheelhousepython -m pip wheel . --wheel-dir wheelhouse

pip wheel 还可以使用:

python -m pip wheel . \  --no-deps \  --no-build-isolation \  --config-settings key=value \  --wheel-dir wheelhouse

这些参数分别控制是否构建依赖、是否使用隔离环境、向后端传递配置以及输出目录;它们不会让项目名保留中划线,也不会凭空改变 ABI 或平台兼容性。

十、检查文件名和内部元数据是否一致

wheel 是 ZIP 格式,可以查看内容:

python -m zipfile -l \  dist/my_demo_package-1.2.0-py3-none-any.whl

至少检查:

  • 文件名中的字段数量和标签是否合理;
  • .dist-info/METADATA
     中的 Name、Version 是否符合预期;
  • .dist-info/WHEEL
     中的 Tag 是否与文件名标签一致;
  • 原生扩展、动态库和资源文件是否全部包含。

还可以执行:

python -m pip install twinepython -m twine check dist/*

最后在干净虚拟环境中安装测试。文件名看起来正确,并不代表内部二进制真的兼容。

总结

一个 wheel 文件名可以看成一张兼容性标签卡:

distribution-version-build-python-abi-platform.whl
  • distribution 决定“这是哪个项目”,项目名中的 -、_、. 在 wheel 文件名中通常转义为 _;
  • version 遵循 PEP 440,版本号的规范化规则与项目名规则不同;
  • build tag 用于区分同版本的多个构建,必须以数字开头;
  • python tag 描述 Python 实现和版本;
  • abi tag 描述二进制接口兼容性;
  • platform tag 描述操作系统、架构和系统 ABI。

构建选项的原则也很简单:

构建选项负责生成真实产物,文件名标签负责声明产物兼容性。

--python-tag、--plat-name、--build-number 等旧式参数可以影响部分字段,但不会替代编译;现代项目优先使用 python -m build --wheel,多平台场景使用构建矩阵或 cibuildwheel。不要通过 mv 手工改 wheel 文件名来伪造项目名、ABI 或平台。

参考资料

  • PEP 427:The Wheel Binary Package Format
  • PEP 440:Version Identification and Dependency Specification
  • PyPA:Binary distribution format
  • build
     文档
  • pip wheel
     文档
  • cibuildwheel 文档

最新文章

随机文章