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

Python 增强提案

PEP 552 – 确定性 pyc 文件

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

目录

摘要

本PEP提议对pyc格式进行扩展,使其更具确定性。

基本原理

可复现构建是指每次构建相同源代码时,即使在不同机器上(当然,前提是它们具有相似的环境设置),也能生成完全相同的输出。可复现性对于安全性至关重要。它也是基于内容的构建系统(如Bazel)的关键概念,当输出文件的内容是输入文件内容的确定性函数时,这类系统最有效。

当前的Python pyc格式是模块的marshaled代码对象,前缀是魔术数字、源文件时间戳和源文件大小。源文件时间戳的存在意味着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在本文主题上的有益交流。


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

最后修改:2025-02-01 08:59:27 GMT