包格式#

此页面讨论用于分发 Python 包的文件格式及其之间的差异。

您将在包索引(例如 PyPI)上找到两种格式的文件:源分发或简称sdist,以及二进制分发,通常称为wheels。例如,pip 23.3.1 的 PyPI 页面允许您下载两个文件,pip-23.3.1.tar.gzpip-23.3.1-py3-none-any.whl。前者是 sdist,后者是 wheel。如下所述,它们具有不同的用途。在 PyPI(或其他地方)上发布包时,您应该始终上传一个 sdist 和一个或多个 wheel。

什么是源分发?#

从概念上讲,源分发是原始形式的源代码存档。具体来说,sdist 是一个 .tar.gz 存档,其中包含源代码以及一个名为 PKG-INFO 的特殊附加文件,其中包含项目元数据。此文件的存在通过不需要打包工具自行计算元数据来帮助提高效率。PKG-INFO 文件遵循 核心元数据规范 中指定的格式,并且不打算手工编写 [1]

因此,您可以通过使用标准工具解压缩 sdist 来检查其内容,这些工具可用于处理 tar 存档,例如 UNIX 平台(如 Linux 和 macOS)上的 tar -xvf,或 Python 的 tarfile 模块的命令行界面 在任何平台上。

Sdist 在打包生态系统中有多种用途。当 pip(标准 Python 包安装程序)找不到要安装的 wheel 时,它将回退到下载源分发,从中编译 wheel,然后安装 wheel。此外,sdist 通常被下游打包程序(例如 Linux 发行版、Conda、Homebrew 和 macOS 上的 MacPorts 等)用作包源,出于各种原因,他们可能更喜欢 sdist,例如从 Git 存储库中提取。

源分发可以通过其文件名识别,其形式为 package_name-version.tar.gz,例如 pip-23.3.1.tar.gz

如果您想要有关 sdist 格式的技术详细信息,请阅读 sdist 规范

什么是 wheel?#

从概念上讲,wheel 确切地包含安装包时需要复制的文件。

对于使用编译语言(例如 C、C++ 和 Rust)编写的带有 扩展模块 的软件包,sdist 和 wheels 之间存在很大差异,这些语言需要编译成与平台相关的机器代码。对于这些软件包,wheels 不包含源代码(例如 C 源文件),而是包含已编译的可执行代码(例如 Linux 上的 .so 文件或 Windows 上的 DLL)。

此外,虽然每个项目版本只有一个 sdist,但可能有多个 wheels。同样,这在扩展模块的上下文中最为相关。扩展模块的编译代码与操作系统和处理器架构相关联,通常还与 Python 解释器的版本相关联(除非使用 Python 稳定 ABI)。

对于纯 Python 软件包,sdist 和 wheels 之间的差异不太明显。通常只有一个 wheel,适用于所有平台和 Python 版本。Python 是一种解释型语言,不需要提前编译,因此 wheels 包含 .py 文件,就像 sdist 一样。

如果您想知道 .pyc 字节码文件:它们不包含在 wheels 中,因为它们很容易生成,并且包括它们将不必要地迫使大量软件包为每个 Python 版本分发一个 wheel,而不是一个 wheel。相反,pip 等安装程序会在安装软件包时生成它们。

话虽如此,即使对于纯 Python 项目,sdist 和 wheels 之间仍然存在重要差异。wheels 的目的是仅包含要安装的内容,而没有更多内容。特别是,wheels 绝不应包含测试和文档,而 sdist 通常包含这些内容。此外,wheel 格式比 sdist 更复杂。例如,它包含一个特殊文件(称为 RECORD),其中列出了 wheel 中的所有文件及其内容的哈希值,作为下载完整性安全检查。

乍一看,您可能想知道对于“简单而基本的”纯 Python 项目是否真的需要 wheels。请记住,由于 sdist 的灵活性,pip 等安装程序无法直接从 sdist 安装 - 它们需要首先通过调用 sdist 指定的 构建后端 来构建一个 wheel(构建后端在构建 wheel 时可能执行各种转换,例如编译 C 扩展)。因此,即使对于纯 Python 项目,您也应始终将 sdist 和 wheel 同时 上传到 PyPI 或其他软件包索引。由于 wheel 可以直接安装,因此这可以大大加快用户安装速度。通过仅包含必须安装的文件,wheels 还可以减小下载大小。

在技术层面上,wheel 是一个 ZIP 存档(与作为 TAR 存档的 sdist 不同)。您可以通过将其解压缩为普通 ZIP 存档来检查其内容,例如,在 Linux 和 macOS 等 UNIX 平台上使用 unzip,在 Windows 上的 Powershell 中使用 Expand-Archive,或 Python zipfile 模块的命令行界面。这对于检查 wheel 是否包含您需要的所有文件非常有用。

在 wheel 中,您将找到软件包的文件,以及一个名为 package_name-version.dist-info 的附加目录。此目录包含各种文件,包括 METADATA 文件,该文件相当于 sdist 中的 PKG-INFO,以及 RECORD。这对于确保您的 wheels 中没有丢失任何文件非常有用。

wheel 的文件名(忽略一些很少使用的功能)如下所示:package_name-version-python_tag-abi_tag-platform_tag.whl。此命名约定标识了 wheel 与哪些平台和 Python 版本兼容。例如,名称 pip-23.3.1-py3-none-any.whl 意味着

  • (py3) 此 wheel 可以安装在任何 Python 3 实现上,无论是 CPython(最广泛使用的 Python 实现)还是替代实现,例如 PyPy

  • () 它不依赖于 Python 版本;

  • (任何) 它不依赖于平台。

模式 py3-none-any 适用于纯 Python 项目。具有扩展模块的包通常会附带多个带有更复杂标记的轮子。

有关轮子格式的所有技术详细信息,请参阅 轮子规范

鸡蛋呢?#

“鸡蛋”是一种旧的包格式,已被轮子格式取代。它不应该再被使用了。自 2023 年 8 月起,PyPI 拒绝鸡蛋上传

以下是轮子和鸡蛋之间重要差异的细分。

  • 鸡蛋格式由 Setuptools 于 2004 年引入,而轮子格式由 PEP 427 于 2012 年引入。

  • 轮子有一个 官方标准规范。鸡蛋没有。

  • 轮子是一个 分发 格式,即一个打包格式。 [2] 鸡蛋既是分发格式,也是运行时安装格式(如果保持压缩),并且被设计为可导入。

  • 轮子存档不包含 .pyc 文件。因此,当分发仅包含 Python 文件(即没有编译的扩展)并且与 Python 2 和 3 兼容时,轮子就有可能成为“通用”的,类似于 sdist

  • 轮子存档使用标准 .dist-info 目录。鸡蛋使用 .egg-info

  • 轮子有一个 更丰富的文件命名约定。单个轮子存档可以指示其与许多 Python 语言版本和实现、ABI 和系统架构的兼容性。

  • 轮子是版本化的。每个轮子文件都包含轮子规范的版本和打包它的实现。

  • 轮子在内部由 sysconfig 路径类型 组织,因此更容易转换为其他格式。