Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

PEP 552 – 确定性 pycs

作者:
Benjamin Peterson <benjamin at python.org>
状态:
最终版
类型:
标准跟踪
创建:
2017 年 9 月 4 日
Python 版本:
3.7
历史记录:
2017 年 9 月 7 日
决议:
Python-Dev 消息

目录

摘要

本 PEP 提出对 pyc 格式的扩展,使其更具确定性。

理由

一个 可重现构建 意味着每次构建相同的源代码时,都会生成相同的字节输出,即使是在不同的机器上(自然地,必须满足机器拥有相当类似的环境配置的要求)。可重现性对安全性至关重要。它也是基于内容的构建系统(如 Bazel)中的关键概念,这些系统在输出文件的内容是输入文件内容的确定性函数时,效率最高。

当前的 Python pyc 格式是模块的已编组代码对象,前面加上一个 魔数、源代码时间戳和源代码文件大小。源代码时间戳的存在意味着 pyc 不是输入文件内容的确定性函数,它还取决于易变的元数据,即源代码的 mtime。因此,pyc 是可重现性的障碍。

Python 代码的分发者目前面临以下选择:

  1. 不分发 pyc,从而失去缓存优势
  2. 分发 pyc,从而失去可重现性
  3. 小心地为所有 Python 源代码文件赋予确定性时间戳(例如,参见 https://github.com/python/cpython/pull/296
  4. 执行 1 和 2 的复杂混合,例如在安装时生成 pyc

这些选项都不太吸引人。本 PEP 建议允许用确定性哈希值替换时间戳。不过,当前的时间戳失效方法将保持为默认值。尽管有非确定性,但时间戳失效方法在许多工作流程和用例中运行良好。基于哈希的 pyc 格式可能会带来读取和哈希所有源代码文件的成本,这比简单地检查时间戳要昂贵。因此,目前,我们预计它主要由分发者和高级用例使用。

(注意,还有其他 [1] [2] 我们在此不讨论的问题可能会导致 pyc 不确定性。)

规范

pyc 标头目前由 3 个 32 位字组成。我们将将其扩展为 4 个。第一个字将继续为魔数,用于对字节码和 pyc 格式进行版本控制。第二个字(概念上是新字)将是一个位字段。pyc 的剩余标头解释和失效行为取决于位字段的内容。

如果位字段为 0,则 pyc 是传统的基于时间戳的 pyc。也就是说,第三和第四个字分别是时间戳和文件大小,失效将通过将源代码文件的元数据与标头中的元数据进行比较来完成。

如果位字段的最低位被设置,则 pyc 是一个基于哈希的 pyc。我们将第二个最低位称为 check_source 标志。在位字段之后是一个 64 位的源代码文件哈希值。我们将使用带有一个硬编码密钥的 SipHash,该密钥为源代码文件的内容。其他快速哈希算法(如 MD5 或 BLAKE2)也可以工作。我们选择 SipHash 是因为 Python 已经从 PEP 456 中内置了其实现,尽管必须向 Python 公开允许选择 SipHash 密钥的接口。哈希的安全不是问题,尽管我们忽略了 MD5 之类的完全失效的哈希算法,以方便在受控环境中对 Python 进行审计。

当 Python 遇到一个基于哈希的 pyc 时,其行为取决于 check_source 标志的设置。如果 check_source 标志被设置,Python 将通过对源代码文件进行哈希并将其与 pyc 中的预期哈希进行比较来确定 pyc 的有效性。如果需要重新生成 pyc,它将再次以基于哈希的 pyc 的形式重新生成,并设置 check_source 标志。

对于 check_source 未设置的基于哈希的 pyc,Python 将简单地加载 pyc,而不会检查源代码文件的哈希值。在这种情况下,期望一些外部系统(例如,本地 Linux 发行版的包管理器)负责维护 pyc 的最新状态,因此 Python 本身不需要进行检查。即使在禁用验证的情况下,也应该正确设置哈希字段,以便带外一致性检查器可以验证 pyc 的最新状态。还要注意,PEP 3147 中关于没有对应源代码文件的 pyc 不应该被加载的规定仍然适用于基于哈希的 pyc。

py_compilecompileall 的编程 API 将支持生成基于哈希的 pyc。主要的是,py_compile 将定义一个新的枚举,对应于所有可用的 pyc 失效模块。

class PycInvalidationMode(Enum):
    TIMESTAMP
    CHECKED_HASH
    UNCHECKED_HASH

py_compile.compilecompileall.compile_dircompileall.compile_file 都将获得一个 invalidation_mode 参数,它接受 PycInvalidationMode 枚举的值。

compileall 工具将扩展一个新的命令行选项 --invalidation-mode,用于生成带有或不带有 check_source 位设置的基于哈希的 pyc。 --invalidation-mode 将是一个三态选项,接受 timestamp(默认值)、checked-hashunchecked-hash 的值,分别对应于 PycInvalidationMode 的值。

importlib.util 将扩展一个 source_hash(source) 函数,该函数计算 pyc 写入代码为字节串 source 使用的哈希值。

基于哈希的 pyc 失效的运行时配置将通过一个新的解释器选项 --check-hash-based-pycs 来实现。这是一个三态选项,它可以接受 3 个值:defaultalwaysnever。默认值 default 意味着基于哈希的 pyc 中的 check_source 标志决定失效,如上所述。 always 会导致解释器对源代码文件进行哈希以进行失效,无论 check_source 位的值如何。 never 会导致解释器始终假设基于哈希的 pyc 有效。当 --check-hash-based-pycs=never 生效时,未经检查的基于哈希的 pyc 将以未经检查的基于哈希的 pyc 的形式重新生成。基于时间戳的 pyc 不会受到 --check-hash-based-pycs 的影响。

参考资料

致谢

作者感谢 Gregory P. Smith、Christian Heimes 和 Steve Dower 对本 PEP 主题的宝贵讨论。


源代码:https://github.com/python/peps/blob/main/peps/pep-0552.rst

最后修改时间:2023 年 9 月 9 日 17:39:29 GMT