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.py
和 two.py
的 alpha
包存在于安装了 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] 中,这个想法首次被提出,并建议了几种替代方案。为了完整起见,它们列在这里,以及不采用它们的原因。
独立目录或符号链接
Debian 和 Ubuntu 可以简单地向 sys.path
添加一个版本特定的目录,其中仅包含该 Python 版本的扩展模块。或者,可以在 PEP 3147 中删除的符号链接技巧仅保留用于共享库。这种方法被拒绝,因为它延续了 PEP 3147 试图避免的基本复杂性,并且可能会为所有模块添加几个额外的目录进行搜索,即使扩展模块的数量远少于 Python 包的总数。例如,构建同时提供了使用和不使用宽 Unicode、使用和不使用 pydebug 以及使用和不使用 pymalloc 的版本,搜索目录的总数大大增加。
参考实现
此代码的工作在 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