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年7月9日
Python 版本:
3.2
发布历史:
2010年7月14日,2010年7月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 文件进行类似的区分。针对不同 Python 主要版本编译的扩展模块由于 ABI 的变化而彼此不兼容。相同 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 链接到它们。符号链接之所以存在,是因为在 pre-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 上的调试构建也已经为动态库使用了不同的文件扩展名,并且实际上在 .dll 文件名中编码了(以与本 PEP 中提议的不同方式)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 使用。

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

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

distutils [6] build_ext 命令也必须扩展,以便在模块作者指示其扩展支持该 ABI 版本时,编译为带有 abi3 标签的共享库文件。这可以通过向 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 中。正在进行中的差异也可以查看 [10],并随着新更改的上传而自动更新。

参考资料


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

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