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

Python 增强提案

PEP 3149 – ABI 版本标记 .so 文件

作者:
Barry Warsaw <barry at python.org>
状态:
最终
类型:
标准跟踪
创建:
2010-07-09
Python 版本:
3.2
历史记录:
2010-07-14, 2010-07-22
决议:
Python-Dev 消息

目录

摘要

PEP 3147 描述了对 Python 导入机制的扩展,通过允许将多个字节编译文件 (.pyc) 与每个源文件共存,从而改进了 Python 源代码的共享。

本 PEP 定义了一个辅助功能,允许以类似的方式共存扩展模块文件 (.so)。此可选的构建时功能将使 Python 的下游发行版更容易同时提供多个 Python 主版本。

背景

PEP 3147 定义了纯 Python 包的文件系统布局,其中系统上可使用多个版本的 Python。例如,当包含源模块 one.pytwo.pyalpha 包存在于安装了 Python 3.2 和 3.3 的系统上时,字节编译后的文件系统布局将是

alpha/
    __pycache__/
        __init__.cpython-32.pyc
        __init__.cpython-33.pyc
        one.cpython-32.pyc
        one.cpython-33.pyc
        two.cpython-32.pyc
        two.cpython-33.pyc
    __init__.py
    one.py
    two.py

对于包含扩展模块的包,模块的 .so 文件也需要类似的区分。由于 ABI 的更改,为不同 Python 主版本编译的扩展模块彼此不兼容。相同 Python 版本的不同配置/编译选项可能会导致不同的 ABI(例如 –with-wide-unicode)。

虽然 PEP 384 定义了一个稳定的 ABI,但它将最大程度地减少,但不会消除 Python 构建或主版本之间扩展模块的不兼容性。因此,提出了一种用于区分扩展模块文件名的机制。

基本原理

Ubuntu [3] 和 Debian [4] 等 Linux 发行版同时为其用户提供多个 Python 版本。例如,Ubuntu 9.10 Karmic Koala 用户可以安装 Python 2.5、2.6 和 3.1,其中 Python 2.6 为默认版本。

为了在可用的 Python 版本之间尽可能多地共享,这些发行版将第三方包模块(.pyc.so 文件)安装到 /usr/share/pyshared 中,并从 /usr/lib/pythonX.Y/dist-packages 中对其进行符号链接。符号链接存在是因为在 PEP 3147 之前的世界(即 < Python 3.2),由各种已安装的 Python 进行字节编译生成的 .pyc 文件将发生文件名冲突。对于 Python 版本 >= 3.2,所有纯 Python 包都可以共享,因为 .pyc 文件将不再导致文件系统命名冲突。消除这些符号链接使 Python 发行版更简单、更健壮。

共享库扩展也出现了类似的情况。由于扩展模块通常对于 foo 扩展模块命名为 foo.so,如果 foo 为多个 Python 版本提供,这些模块也会发生文件名冲突。

此外,由于相同 Python 版本的不同配置/编译选项会导致向扩展模块呈现不同的 ABI。例如,在 POSIX 系统上,配置选项 --with-pydebug--with-pymalloc--with-wide-unicode 都会更改 ABI。本 PEP 建议在 .so 扩展模块文件的名称中对构建时选项进行编码。

PyPy [5] 也可以从本 PEP 中受益,从而避免为其 API 构建但具有不同 .so 标记的扩展模块中的名称冲突。

提案

在 Python 解释器构建时选择的配置/编译选项将编码在扩展模块的共享库文件名中。此“标记”将出现在模块基本名称和共享库的操作文件系统扩展名之间。

以下信息**必须**包含在共享库文件名中

  • Python 实现(例如 cpython、pypy、jython 等)
  • 解释器的主版本号和次版本号

这两个字段用连字符分隔,并且主版本号和次版本号之间不出现点。例如 cpython-32

Python 实现**可以**根据需要在文件名标记中包含其他标志。例如,在 POSIX 系统上,这些标志也将添加到文件名中

  • --with-pydebug(标记:d
  • --with-pymalloc(标记:m
  • --with-wide-unicode(标记:u

在 Python 3.2 中,默认情况下,configure 会启用 --with-pymalloc,因此共享库文件名将显示为 foo.cpython-32m.so。当其他两个标志也启用时,文件名将为 foo.cpython-32dmu.so

共享库文件名标记无条件使用;它不能更改。标记和扩展模块后缀可通过 sysconfig 模块通过以下变量获得

>>> sysconfig.get_config_var('EXT_SUFFIX')
'.cpython-32mu.so'
>>> sysconfig.get_config_var('SOABI')
'cpython-32mu'

请注意,$SOABI 仅包含标记,而 $EXT_SUFFIX 包括共享库文件的平台扩展名,并且是添加到扩展模块名称的确切后缀。

对于任意包 foo,当安装发行版包时,您可能会看到这些文件

/usr/lib/python/foo.cpython-32m.so
/usr/lib/python/foo.cpython-33m.so

(这些路径仅用于示例目的。发行版可以自由使用任何他们选择的文件系统布局,并且本 PEP 中没有任何内容更改 Python 从源代码构建的安装位置。)

Python 的动态模块加载器将识别并导入与构建时选项匹配的标记的共享库扩展模块。为了向后兼容,Python 还将继续导入未标记的扩展模块,例如 foo.so

此共享库标记将全局用于所有基于 distutils 的扩展模块,而不管它们在文件系统上的哪个位置构建。通过 distutils 以外的其他方式构建的扩展模块要么必须手动计算标记,要么回退到未标记的 .so 文件名。

已验证的方法

从某种意义上说,此处描述的方法已经在 Debian 和 Ubuntu 系统上得到了验证,在这些系统上,不同的扩展用于 Python 和扩展模块的调试构建。Windows 上的调试构建也已经对动态库使用不同的文件扩展名,实际上(以与本 PEP 中提出的不同方式)在 .dll 文件名中编码了 Python 主版本和次版本。

Windows

本 PEP 仅解决使用 configure 脚本的 POSIX 系统上的构建问题。虽然本 PEP 没有明确禁止 Windows 或其他平台的支持,但需要平台专业知识才能评估、描述和实现对这些平台的支持。目前尚不清楚本 PEP 中的功能是否对 Windows 有用。

PEP 384

PEP 384 为扩展模块定义了一个稳定的 ABI。理论上,普遍采用 PEP 384 将消除对本 PEP 的需求,因为所有扩展模块都可能与任何 Python 版本兼容。当然,在实践中,不可能实现普遍采用,如上所述,不同的构建时标志仍然会影响 ABI。因此,即使使用稳定的 ABI,本 PEP 也可能仍然是必要的。虽然完整的规范保留给 PEP 384,但这里讨论了相关问题。

PEP 384 描述了对 PyModule_Create() 的更改,如果扩展模块使用 Py_LIMITED_API 编译,则将 3 作为 API 版本传递。这应该正式化为一个名为 PYTHON_ABI_VERSION 的官方宏,以反映 PYTHON_API_VERSION。如果并且当 ABI 以不兼容的方式更改时,此版本号将增加。为了便于共享,Python 将扩展为搜索其名称中包含 PYTHON_ABI_VERSION 编号的扩展模块。前缀 abi 保留供 Python 使用。

因此,当 Python 使用默认标志集进行配置时,PEP 384 的初始实现将在导入扩展模块 foo 时搜索以下文件名(按此顺序)

foo.cpython-XYm.so
foo.abi3.so
foo.so

distutils [6] build_ext 命令也必须扩展为编译到带有 abi3 标记的共享库文件,当模块作者指示其扩展支持该版本的 ABI 时。这可以通过向 Extension 类添加一个关键字参数以向后兼容的方式完成,例如

Extension('foo', ['foo.c'], abi=3)

Martin v. Löwis 描述了他关于本 PEP 对 PEP 384 的适用性的想法 [7]。总之

  • --with-pydebug 将不受稳定 ABI 支持,因为这会更改 PyObject 的布局,后者是一个公开的结构。
  • --with-pymalloc 与此问题无关。
  • --with-wide-unicode 比较棘手,不过 Martin 倾向于强制稳定的 ABI 使用与平台 wchar_t 匹配的 Py_UNICODE

备选方案

在最初的 python-dev 线程 [8] 中,这个想法首次被提出,并建议了几种替代方案。为了完整起见,它们列在这里,以及不采用它们的原因。

不与扩展模块共享包

有人建议不要在发行版上所有受支持的 Python 版本之间共享包含扩展模块的 Python 包。即使采用了 PEP 3149,也必须为每个受支持的 Python 版本编译扩展模块,因此也许共享此类包本身就没有用处。但是,出于多种原因,不共享包含扩展的包是不可行的。

如果一个纯 Python 包在一个版本中共享,那么如果下一个版本为了速度添加了一个扩展模块,它是否应该突然不共享?此外,尽管所有扩展共享库都将为每个受支持的 Python 版本编译和分发一次,但复制 .so 文件与复制所有 .py 文件之间存在很大差异。额外的大小增加了此类软件包的下载时间,并且更直接地增加了已经受限的发行版 CD-ROM 上的空间压力。

参考实现

此代码的工作在 Launchpad 上的一个 Bazaar 分支 [9] 中跟踪,直到它准备好合并到 Python 3.2 中。正在进行的工作 diff 也可以查看 [10],并在上传新的更改时自动更新。

参考文献


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

上次修改时间: 2023-09-09 17:39:29 GMT