PEP 784 – 将 Zstandard 添加到标准库
- 作者:
- Emma Harper Smith <emma at python.org>
- 发起人:
- Gregory P. Smith <greg at krypto.org>
- 讨论至:
- Discourse 帖子
- 状态:
- 最终版
- 类型:
- 标准跟踪
- 创建日期:
- 2025年4月6日
- Python 版本:
- 3.14
- 发布历史:
- 2025年4月7日
- 决议:
- 2025年4月25日
摘要
Zstandard 是一种被广泛采用、成熟且高效的压缩标准。本 PEP 建议在 Python 标准库中添加一个新模块,其中包含 Meta 的 zstd
库(默认实现)的 Python 封装。此外,为了避免与 PyPI 上的包发生名称冲突,并为 Python 用户提供统一的接口,标准库中的压缩模块将移至 compression.*
包下。
动机
CPython 包含多种不同压缩格式的模块,例如 zlib (DEFLATE)
、gzip
、bzip2
和 lzma
,每种都广泛使用。包含流行的压缩算法符合 Python “batteries included” 的哲学,即包含广泛有用的标准和实用程序。lzma
是最新添加到 Python 3.3 的此类模块。
自那时以来,Zstandard 已成为现代**事实上的**首选压缩库,既可实现高性能压缩和解压缩,又能在合理的 CPU 和内存成本下获得高压缩比。Zstandard 实现了比 bzip2 或 zlib (DEFLATE) 更高的压缩比,同时解压缩速度明显快于 LZMA。
Zstandard 已在计算的许多不同领域中广泛采用。众多的硬件实现表明了对 Zstandard 的长期承诺,并期望 Zstandard 在未来几年仍将是压缩的**事实上的**首选。Zstandard 在 **RFC 8478** 中通过 IETF 标准化进一步证明了这一点。Zstandard 压缩也应用于 ZFS 和 Btrfs 文件系统。
Zstandard 高效的压缩已取代其他现代压缩格式,例如 brotli、lzo 和 ucl,这归因于其高效的压缩。虽然 LZ4 仍在非常高吞吐量的场景中使用,但 Zstandard 也可以在其中一些上下文中使用。虽然将 LZ4 纳入范围之外,但它将是本 PEP 引入的 compression
命名空间未来一个引人注目的补充。
PyPI 上有几个 Zstandard 的 Python 绑定,每个都有不同的 API 和绑定 zstd
库的方式。在标准库中引入官方模块的一个目标是减少 Python 用户在寻找 Zstandard 简单压缩/解压缩 API 时的困惑。现有包可以继续提供扩展 API 或集成较新 Zstandard 版本的功能。
将 Zstandard 支持添加到标准库的另一个原因是解决一个长期存在的开放问题(python/cpython#81276),该问题要求在 tarfile
模块中支持 Zstandard。这个问题在 CPython 跟踪器上的开放问题中获得了第五多的“点赞”,并引发了大量的讨论和兴趣。此外,ZIP 格式标准化了 Zstandard 压缩格式 ID,与 zipfile
模块的集成将允许使用 Zstandard 压缩打开 ZIP 归档。本 PEP 的参考实现包含与 zipfile
、tarfile
和 shutil
模块的集成。
Zstandard 压缩还可以用于减小 Python wheel 包的大小,并显著加快安装速度。Anaconda 发现,当采用 Zstandard 用于 conda 包格式时,速度有显著提升。
Conda 的下载大小减少了约 30-40%,并且解压速度显著加快。 […] 我们看到了大约 2.5 倍的整体加速,这几乎全部归功于新文件格式中使用的 zstd 压缩的解压速度显著加快。
根据 lzbench(一个对许多不同压缩库和格式进行全面基准测试的工具),与 wheel 现有基于 zlib 的压缩相比,Zstandard 具有显著更高的压缩比。虽然本 PEP**不**规定对 wheel 格式或其他打包标准进行任何更改,但标准库中包含 Zstandard 绑定将使未来的 PEP 能够改善 Python wheel 包的用户体验。
基本原理
引入 compression
包
PyPI 上的项目已经占用了 zstd
和 zstandard
这两个导入名称。为了避免破坏现有绑定用户,本 PEP 建议为压缩库引入一个新的命名空间,即 compression
。这个名称在 PyPI 上已为标准库保留。新的 Zstandard 模块将被命名为 compression.zstd
。其他压缩模块将在新的 compression
包中重新导出。
为压缩模块提供一个公共命名空间有几个优点。首先,它减少了用户对在哪里找到压缩模块的困惑。其次,顶层 compression
模块可以提供有关可用压缩格式的信息,类似于 hashlib
的 algorithms_available
。如果 PEP 775 被接受,也可以提供 compression.algorithms_guaranteed
,列出 zlib
。最后,compression
命名空间可以防止未来将其他压缩格式合并到标准库中的问题。新的压缩格式可能会在集成到 CPython 之前发布到 PyPI。因此,任何新的压缩格式导入名称在模块被考虑包含在 CPython 中时可能已经占用。将压缩模块放在包前缀下可以防止未来潜在的名称冲突问题。
希望在不同 Python 版本之间保持兼容的代码可以使用以下模式来确保兼容性
try:
from compression.lzma import LZMAFile
except ImportError:
from lzma import LZMAFile
这将使用较新的导入名称(如果可用),否则将回退到旧名称。
基于 pyzstd
的实现
本 PEP 的实现基于 pyzstd 项目。选择该项目的原因是其代码最初由 Ma Lin 编写,旨在上游到 CPython,他也是当今标准库中使用的输出缓冲区实现的作者。该项目此后由 Rogdham 接管,并发布到 PyPI。pyzstd
中的 API 类似于标准库中其他压缩模块(如 bz2
和 lzma
)的 API。
最低支持的 Zstandard 版本
最低支持的 Zstandard 版本选择为 v1.4.5,发布于 2020 年 5 月。选择此版本作为最低版本是基于对许多 Linux 发行版软件包仓库中可用 Zstandard 版本(包括 LTS 版本)的审查。此版本选择相当保守,以最大程度地与现有 LTS Linux 发行版兼容,但考虑到较新的 Python 版本通常作为较新发行版的一部分进行打包,因此可能会选择较新的 Zstandard 版本。
规范
compression
命名空间
将引入一个新的压缩模块命名空间,名为 compression
。该包的顶层模块最初将为空,但未来可能会在顶层添加用于与压缩例程交互的标准 API。
compression.zstd
模块
将引入一个新模块 compression.zstd
,其中包含与标准库中其他压缩模块(即)匹配的 Zstandard 压缩 API
compress()
/decompress()
- 用于一次性压缩或解压缩的 APIZstdFile
/open()
- 用于与流和文件类对象交互的 APIZstdCompressor
/ZstdDecompressor
- 用于增量压缩或解压缩的 API
它还将包含一些 Zstandard 特定的功能
ZstdDict
/train_dict()
/finalize_dict()
- 用于与 Zstandard 字典交互的 API,这对于压缩许多相似的小数据块非常有用
libzstd
可选依赖项
libzstd
库将成为 CPython 的可选依赖项。如果该库不可用,compression.zstd
模块将不可用。这在 Unix 平台上作为正常构建环境检测的一部分自动处理。
在 Windows 上,libzstd
将添加到 用于构建 CPython 依赖的库的源依赖项中。
其他压缩模块
新的导入名称 compression.lzma
、compression.bz2
、compression.gzip
和 compression.zlib
将在 Python 3.14 中引入,分别重新导出现有 lzma
、bz2
、gzip
和 zlib
模块的内容。compression
子模块将成为未来规范的导入名称。当最低支持的 Python 版本要求允许时,Python 文档中将推广使用新的压缩名称而不是原始的顶层模块名称。
鉴于 _compression
模块被标记为私有,它将立即重命名为 compression._common._streams
。选择新名称是由于该模块的当前内容是标准库压缩模块中与 I/O 相关的流 API 帮助程序(例如 LZMAFile
)。
向后兼容性
本 PEP 不引入向后不兼容的更改。目前没有计划弃用或移除现有压缩模块。现有模块的任何弃用或移除都留待未来决定,但不会早于本 PEP 接受之日起 5 年。
安全隐患
与任何新的 C 代码一样,特别是处理可能不受信任的用户输入的代码,存在内存安全问题的风险。作者计划贡献与 libfuzzer 的集成,以实现对 _zstd
代码的模糊测试,并确保其健壮性。此外,还有许多测试用于验证压缩和解压缩例程。这些测试在使用 AddressSanitizer 编译时无错误通过。
承担一个新的依赖项也总是存在安全风险,但 zstd
库已成熟,每次提交都经过模糊测试,并且参与 Meta 的漏洞奖励计划。
如何教授此内容
新模块的文档在参考实现分支中。现有模块的文档也将更新以引用新名称。
参考实现
参考实现包含 _zstd
C 代码、compression.zstd
代码、对 tarfile
、shutil
和 zipfile
的修改,以及每个新 API 和集成测试。它还包含其他压缩模块的重新导出。
被拒绝的想法
将模块命名为 zstdlib
,并且不创建新的 compression
命名空间
不创建新的 compression
命名空间,而是寻找一个不同的名称,例如 zstdlib
作为导入名称,这是其中一个选项。也提出了其他几个名称,例如 zst
、libzstd
和 zstdcomp
。在讨论中,这些名称要么太容易打错,要么不直观。此外,现有导入名称的问题可能会在未来添加到标准库的压缩格式中持续存在。LZ4 是一种常见的告诉压缩格式,在 PyPI 上有一个包 lz4
,其导入名称为 lz4
。与其为每种压缩格式解决这个问题,不如通过使用已经占用的 compression
命名空间一劳永逸地解决它。
在 Python 3.14 中引入一个实验性的 _zstd
包
由于本 PEP 在 Python 3.14 新功能截止日期临近时发布,一个建议是将包命名为私有模块 _zstd
,以便打包工具可以更快地使用它,但未决定最终名称。这将为 3.15 开发期间讨论最终模块名称留出更多时间。然而,引入私有模块并不受欢迎。标准库中私有模块的外部使用预期和约定尚不明确。
引入一个标准库命名空间而不是 compression
除了 compression
命名空间之外,另一种选择是为整个标准库引入一个 std
命名空间。然而,这被认为是 3.14 版本的一个过于重大的变化,没有达成一致的语义、迁移路径或包名称。此外,未来引入 std
命名空间的 PEP 总是可以定义将 compression
子模块扁平化到 std
命名空间中。
将 zipfile
和 tarfile
包含在 compression
中
压缩通常与归档工具一起使用,因此将 zipfile
和 tarfile
都放在 compression
命名空间下很有吸引力。然而,压缩不仅仅用于归档工具。例如,网络请求可以进行 gzip 压缩。此外,像 tar 这样的格式本身不包含压缩,而是依赖外部压缩。因此,本 PEP 不建议将 zipfile
或 tarfile
移到 compression
下。
不将 gzip
包含在 compression
下
**GZip 格式 RFC** 定义了一种格式,可以包含多个块和关于其内容的元数据。通过这种方式,GZip 与 ZIP 和 tar 等归档格式非常相似。尽管如此,在使用中,GZip 通常被视为一种压缩格式而非归档格式。从不同语言如何对 GZip 进行分类来看,普遍趋势是将其分类为压缩格式而非归档格式。
此外,Python 中的 gzip
模块主要关注单块内容,并且其 API 与其他压缩模块相似,使其非常适合 compression
命名空间。
版权
本文档置于公共领域或 CC0-1.0-Universal 许可证下,以更宽松者为准。
来源:https://github.com/python/peps/blob/main/peps/pep-0784.rst