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

Python 增强提案

PEP 304 – 控制字节码文件生成

作者:
Skip Montanaro
状态:
已撤回
类型:
标准跟踪
创建日期:
2003年1月22日
发布历史:
2003年1月27日,2003年1月31日,2005年6月17日

目录

历史注记

尽管此原始PEP已被撤回,但此功能的一个变体最终在Python 3.8中通过https://bugs.python.org/issue33499实现。

在此PEP中最初提出的一些问题和担忧已在过去几年中通过其他更改得到解决

  • 引入隔离模式以处理潜在的安全问题
  • 切换到importlib,一个完全基于导入钩子的导入系统实现
  • PEP 3147更改了字节码缓存布局,以使用__pycache__子目录,包括source_to_cache(path)cache_to_source(path) API,允许解释器自动处理重定向到单独的缓存目录。

摘要

本PEP概述了一种控制已编译Python字节码文件生成和位置的机制。这个想法最初是作为一个补丁请求[1]提出的,并演变为python-dev邮件列表上的讨论线程[2]。引入一个环境变量将允许安装Python或基于Python的第三方软件包的人员控制在安装时是否应生成字节码文件,如果生成,则应写入何处。它还将允许用户控制在应用程序运行时是否应生成字节码文件,如果生成,则应写入何处。

提案

向Python理解的环境变量中添加一个新的环境变量PYTHONBYTECODEBASE。PYTHONBYTECODEBASE的解释如下

  • 如果未定义,Python字节码的生成方式与当前完全相同。sys.bytecodebase设置为根目录(在Unix和Mac OSX上为/,或在Windows上为启动(安装???)驱动器的根目录——通常是C:\)。
  • 如果已定义且它指向用户具有写入权限的现有目录,则sys.bytecodebase设置为该目录,并且字节码文件将写入以该位置为根的目录结构中。
  • 如果已定义但为空,则sys.bytecodebase设置为None,并完全禁止字节码文件的生成。
  • 如果已定义并且以下之一为真
    • 它不指向目录,
    • 它指向一个目录,但用户没有写入权限

    将显示警告,sys.bytecodebase设置为None,并完全禁止字节码文件的生成。

启动初始化后,所有运行时引用都指向sys.bytecodebase,而不是PYTHONBYTECODEBASE环境变量。sys.path未修改。

从上面我们看到,sys.bytecodebase只能取两种有效类型的值:None或一个字符串,指向系统上一个有效的目录。

在导入过程中,此扩展的工作方式如下:

  • 进行模块的正常搜索。搜索顺序大致是:动态加载的扩展模块、Python源文件、Python字节码文件。只有当找到Python源文件时,此机制才会发挥作用。
  • 一旦找到源模块,将尝试读取同一目录中的字节编译文件。(这与之前相同。)
  • 如果未找到字节编译文件,则尝试从增强目录中读取字节编译文件。
  • 如果需要生成字节码,则生成的字节码将尽可能写入增强目录。

请注意,本PEP明确**不**涉及提供逐模块或逐目录控制字节码文件处置的机制。

词汇表

  • “字节码基准”指sys.bytecodebase的当前设置。
  • “增强目录”指由字节码基准和源文件目录名组成的目录。
  • PYTHONBYTECODEBASE在需要与“字节码基准”区分时指代环境变量。

定位字节码文件

当解释器搜索模块时,它将像往常一样使用sys.path。然而,当考虑可能的字节码文件时,可能会进行额外的字节码文件探测。首先,使用sys.path中包含源文件的目录检查字节码文件(当前行为)。如果那里未找到有效的字节码文件(或者不存在,或者存在但已过时),并且字节码基准不是None,则会使用sys.path中适当以字节码基准为前缀的目录进行第二次探测。

写入字节码文件

当字节码基准不是None时,新的字节码文件将写入相应的增强目录,而不会直接写入sys.path中的目录。

定义增强目录

从概念上讲,字节码文件的增强目录是源文件存在的目录,以字节码基准为前缀。在Unix环境中,这将是

pcb = os.path.abspath(sys.bytecodebase)
if sourcefile[0] == os.sep: sourcefile = sourcefile[1:]
augdir = os.path.join(pcb, os.path.dirname(sourcefile))

在没有单根目录树的Windows上,包含源文件的目录的驱动器号在移除尾随冒号后被视为目录组件。因此,增强目录的派生方式为

pcb = os.path.abspath(sys.bytecodebase)
drive, base = os.path.splitdrive(os.path.dirname(sourcefile))
drive = drive[:-1]
if base[0] == "\\": base = base[1:]
augdir = os.path.join(pcb, drive, base)

固定字节码基准位置

在程序启动期间,PYTHONBYTECODEBASE环境变量的值被设置为绝对路径,进行有效性检查并添加到sys模块中,实际上是

pcb = os.path.abspath(os.environ["PYTHONBYTECODEBASE"])
probe = os.path.join(pcb, "foo")
try:
    open(probe, "w")
except IOError:
    sys.bytecodebase = None
else:
    os.unlink(probe)
    sys.bytecodebase = pcb

这允许用户将字节码基准指定为相对路径,但在程序执行期间不会受当前工作目录更改的影响。(我无法想象你希望它在程序执行期间四处移动。)

sys.bytecodebase没有什么特别之处。用户可以根据需要更改其运行时值,但通常不会修改。

基本原理

在许多环境中,非root用户无法写入包含Python源文件的目录。大多数情况下,这不是问题,因为Python源通常在安装期间进行字节编译。但是,在某些情况下,字节码文件缺失或需要更新。如果包含源文件的目录对当前用户不可写,则每次运行导入该模块的程序时都会产生性能损失。[3] 在某些情况下也可能生成警告消息。如果目录可写,则两个单独的进程可能几乎同时尝试写入字节码文件,从而导致文件损坏。[4]

在可使用RAM磁盘的环境中,出于性能原因,可能需要将字节码文件写入此类磁盘上的目录。同样,在Python源代码位于网络文件系统的环境中,可能需要将字节码文件缓存到本地磁盘上。

备选方案

迄今为止提出的唯一其他替代方案[1]似乎是向解释器添加-R标志,以完全禁用字节码文件的写入。本提案涵盖了这一点。添加命令行选项当然是可能的,但可能不够,因为解释器的命令行在安装期间(程序启动早期???)不容易获得。

问题

  • 模块的__file__属性的解释。我相信模块的__file__属性应该反映字节码文件的真实位置。如果人们想找到模块的源代码,他们应该使用imp.find_module(module)。
  • 安全性 - 如果root用户设置了PYTHONBYTECODEBASE怎么办?是的,这可能会带来安全风险,但root用户所做的许多其他事情也可能带来安全风险。root用户可能不应该设置PYTHONBYTECODEBASE,除非可能在安装期间。不过,也许可以尽量减少这个问题。作为root用户运行时,解释器应该检查PYTHONBYTECODEBASE是否指向除root用户之外的任何人可写的目录。如果是,则可以引发异常或警告并将sys.bytecodebase设置为None。或者,请参阅下一项。
  • 更多安全问题 - 如果PYTHONBYTECODEBASE指向一个通用目录(比如/tmp)怎么办?在这种情况下,也许只有当文件归当前用户或root用户所有时,才应该加载预先存在的字节码文件。(这在Windows上重要吗?)
  • 本PEP与导入钩子的交互尚未考虑。事实上,实现这个想法的最佳方式可能是一个导入钩子。参见PEP 302
  • 在当前(PEP 304之前)环境中,在创建相应的字节码文件后,删除源文件是安全的,因为它们位于同一目录中。而当前定义的PEP 304并非如此。增强目录中的字节码文件仅在源文件存在时才被考虑,因此在查找以“.pyc”结尾的模块文件时,它从不被考虑。我认为这种行为可能需要改变。

示例

在下面的示例中,urllib 源代码位于 `/usr/lib/python2.3/urllib.py`,并且 `/usr/lib/python2.3` 位于 `sys.path` 中,但当前用户不可写。

  • 字节码基准是/tmp。/usr/lib/python2.3/urllib.pyc存在且有效。当导入urllib时,使用/usr/lib/python2.3/urllib.pyc的内容。不查阅增强目录。不生成其他字节码文件。
  • 字节码基准是/tmp。/usr/lib/python2.3/urllib.pyc存在,但已过时。当导入urllib时,生成的字节码文件将写入增强目录中的urllib.pyc,其值为/tmp/usr/lib/python2.3。中间目录将根据需要创建。
  • 字节码基准为 None。未找到 urllib.pyc 文件。导入 urllib 时,不写入任何字节码文件。
  • 字节码基准是/tmp。未找到urllib.pyc文件。当导入urllib时,生成的字节码文件将写入增强目录,其值为/tmp/usr/lib/python2.3。中间目录将根据需要创建。
  • 启动时,PYTHONBYTECODEBASE 为 /tmp/foobar,它不存在。会发出警告,sys.bytecodebase 被设置为 None,并且在程序执行期间不会写入任何字节码文件,除非 sys.bytecodebase 稍后更改为指向一个有效且可写的目录。
  • 启动时,PYTHONBYTECODEBASE 设置为 /,该目录存在,但当前用户不可写。将发出警告,sys.bytecodebase 设置为 None,并且在程序执行期间不会写入任何字节码文件,除非 sys.bytecodebase 稍后更改为指向有效且可写的目录。请注意,即使为特定字节码文件构建的增强目录可能对当前用户可写,但重要的是字节码基准目录本身是可写的。
  • 启动时PYTHONBYTECODEBASE设置为空字符串。sys.bytecodebase设置为None。然而,不生成警告。如果在导入urllib时未找到urllib.pyc文件,则不写入字节码文件。

在接下来的Windows示例中,urllib 源代码位于C:\PYTHON22\urllib.pyC:\PYTHON22位于sys.path中,但当前用户不可写。

  • 字节码基准设置为C:\TEMPC:\PYTHON22\urllib.pyc存在且有效。导入urllib时,使用C:\PYTHON22\urllib.pyc的内容。不查阅增强目录。
  • 字节码基准设置为C:\TEMPC:\PYTHON22\urllib.pyc存在,但已过时。导入urllib时,新的字节码文件将写入增强目录,其值为C:\TEMP\C\PYTHON22。中间目录将根据需要创建。
  • 启动时,PYTHONBYTECODEBASE 设置为 TEMP,应用程序启动时当前工作目录为 H:\NET。因此,潜在的字节码基准为 H:\NET\TEMP。如果此目录存在且当前用户可写,则 sys.bytecodebase 将设置为该值。如果不是,将发出警告,sys.bytecodebase 将设置为 None。
  • 字节码基准是C:\TEMP。未找到urllib.pyc文件。当urllib被导入时,生成的字节码文件将写入增强目录,其值为C:\TEMP\C\PYTHON22。中间目录将根据需要创建。

实施

请参阅 Sourceforge 上的补丁。[6]

参考资料


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

上次修改时间:2025-02-01 08:59:27 GMT