PEP 778 – 支持 Wheel 中的符号链接
- 作者:
- Emma Harper Smith <emma at python.org>
- 发起人:
- Barry Warsaw <barry at python.org>
- PEP 代理人:
- Paul Moore <p.f.moore at gmail.com>
- 讨论至:
- Discourse 帖子
- 状态:
- 推迟
- 类型:
- 标准跟踪
- 主题:
- 打包
- 要求:
- 777
- 创建日期:
- 2024年5月18日
- 发布历史:
- 2024年10月10日
摘要
目前 Wheel 对符号链接的处理不佳,安装时会复制内容而不是创建符号链接。为了正确处理 Wheel 中库的分发,我们提出了一个新的 LINKS
元数据文件,以平台可移植的方式处理符号链接。此规范要求一个新的 Wheel 主版本,详情在 PEP 777 中讨论。
PEP 延期
此 PEP 已被推迟,直到建立更好的 Wheel 格式重大更改兼容性方案。一旦为 Wheel 建立了兼容性方案,允许以不显眼的方式引入向后不兼容的行为,本 PEP 中应解决以下几点:
- 将此主题重新聚焦于 POSIX 平台上共享库的符号链接,或许与平台标签绑定?
- 符号链接应该作为文件属性在存档中实现,还是作为
LINKS
文件?是否可以将其编码在RECORD
中? - 澄清此 PEP 不足以用于 PEP 660 可编辑安装,因为它将不再跨平台。
- 描述在 POSIX 平台上符号链接不可用时的回退行为。
动机
目前,Wheel 中的符号链接被创建为文件副本,因为 CPython 中的 zipfile 模块出于安全原因不支持就地处理符号链接。
这给那些希望在 Wheel 中发布大型编译库的项目带来了问题,因为它们必须选择要么大幅增加项目在磁盘上的安装大小,要么省略符号链接并可能破坏一些下游用例。
为了发布一个可以正确加载以供 POSIX 上的运行时使用或构建时链接的库,该库应遵循 POSIX 风格加载器和链接器搜索的约定。加载器使用的两个主要文件名是“soname”和“real name”。“soname”是一个像 libfoo.so.3
这样的文件,其中 3
是一个数字,当库的接口更改时会递增。“real name”是一个名为 libfoo.so.3.1.4
的文件,其中额外的版本信息允许加载器找到特定版本的库。最后,当编译代码以链接到库时,链接器会搜索“链接器名称”,其名称像 libfoo.so
。在这篇关于共享库的 Linux 文档中有更详细的描述。为了完全支持所有运行时和构建时用例,一个项目需要发布所有 3 个文件。通常,这在 POSIX 平台上通过使用符号链接来处理,这样库就不会在磁盘上重复 3 次。
回到 Python 打包,有许多流行的项目发布二进制库,例如 numpy
、scipy
和 pyarrow
。其他 site-packages 会 dlopen
其他 Wheel 中的库,例如 pytorch
和 jax
。这些项目目前依赖于 Wheel 中的单个库,但这可能会导致链接器找到错误的库,如果存在具有“真实名称”库版本的系统库。
符号链接在 Wheel 中也可能带来潜在的好处,即通过简单地在用户的 site-packages
目录中放置一个符号链接来简化可编辑安装,但本 PEP 将此作为一个有待在未来 PEP 中探讨的开放问题。
基本原理
为了支持 POSIX 上加载和库链接中使用的库的 3 种主要命名方式,我们建议在 Python Wheel 中添加对符号链接的支持。为了跟踪已创建的符号链接,并可能支持其他可能不直接支持 POSIX 符号链接的平台,我们建议使用一个新的 Wheel 元数据文件 LINKS
,它将存在于 .dist-info
目录中,与 METADATA
、RECORD
和其他元数据文件并列。
使用 LINKS
文件将允许更多跨平台使用类似符号链接的功能。在 Windows 上,符号链接需要允许用户创建符号链接的组策略(例如,通过启用开发人员模式)或管理员权限。这意味着在某些用户系统上可能不支持符号链接。通过使用 LINKS
文件,安装程序将能够潜在地使用其他方法来处理符号链接,例如 Windows 上的 junctions,否则安装程序将不得不失败。
本 PEP 还描述了安装程序在安装更新的 Wheel 时必须执行的检查。这些检查旨在处理允许 Wheel 安装符号链接所带来的安全风险。有关这些检查为何重要的更多信息,请参阅安全隐患。
规范
Wheel 主版本更新
本 PEP 要求对 Wheel 主版本进行更新,因此使用 LINKS
生成的 Wheel 的 Wheel-Version
必须至少为版本 2.0
,以便旧的安装程序不会默默地安装符号链接失败并破坏用户环境。更多信息请参阅 PEP 777。
新的 LINKS
元数据文件
为了启用跨平台符号链接,本 PEP 引入了一个新的 Wheel 元数据文件 LINKS
。以下是 LINKS
文件的一个示例:
my_package/libfoo.so.3.1.4,my_package/libfoo.so.3
my_package/libfoo.so.3,my_package/libfoo.so
如上所示,LINKS
的格式是 source_path,target_path
,其中 source_path
是相对于 Wheel 中任何命名空间或包根目录的路径。target_path
是 Wheel 中任何包的命名空间或包中的一个 *非悬空* 路径(即,Wheel 内容提取后文件系统上存在的路径)。这意味着如果一个 Wheel 包含多个包,则 Wheel 中包的所有路径都是可接受的。
安装程序行为规范
安装程序在决定任何 source_path
或 target_path
是否有效 *之前* 必须解析 LINKS
文件中包含的任何链接的路径。安装程序 必须 验证 source_path
和 target_path
是否位于来自 Wheel 的任何命名空间或包内部。安装程序 必须 拒绝 Wheel 中的循环符号链接。如果一长串符号链接(符号链接多次指向符号链接)超出了安装程序设置的限制,安装程序 可以 报错。
安装程序在处理带有符号链接的 Wheel 时 必须 遵循以下步骤:
- 检查
.dist-info
中是否存在LINKS
文件。如果不存在,则无需进一步操作。 - 按照 Wheel 1.x 的方式提取 Wheel 包和数据目录中的所有文件。
- 验证对于每个
source_path
和target_path
对,target_path
是否存在于刚提取的包命名空间之一中。 - 接下来,检查安装程序是否可以在 site 目录中为每对创建某种链接。如果安装程序无法为当前平台创建文件/文件夹
target_path
的链接,则 必须 报错。例如,如果 POSIX 符号链接指向文件目标,而安装程序在 Windows 上运行且无法创建符号链接但可以创建 junctions。在这种情况下,安装程序 必须 报错,因为它无法处理该链接。 - 最后,安装程序 必须 在
source_path
和target_path
之间添加一个与平台相关的链接。
安装程序在处理符号链接时 默认不得 复制文件而不是生成符号链接。安装程序 可以 在替代配置或命令行标志下提供此类行为。
构建后端规范
在创建 Wheel 时,构建后端在决定是否将符号链接包含在 Wheel 中时 必须 以与其目标相同的方式对待符号链接。构建后端 必须 验证 LINKS
文件中没有悬空符号链接。构建后端 应该 识别将包含在构建中的与平台相关的符号链接。在 POSIX 系统上,这通常是符号链接;在 Windows 上,这包括符号链接和 junctions。
向后兼容性
引入符号链接将需要 Wheel 格式主版本递增。这意味着使用新 Wheel 格式的新 Wheel 将在旧的安装程序工具上引发错误,根据Wheel 规范。
请参阅关于“Wheel 2.0”的 PEP 777。
安全隐患
如果处理不当,符号链接可能非常危险。一个简单的例子是,如果用户运行 sudo pip install malicious
,并且没有保护措施,那么恶意包可能会覆盖 /etc/shadow
并替换系统上的密码哈希,从而允许恶意登录。
本 PEP 列出了安装程序对 Wheel 中的符号链接进行检查的几项要求,以确保上述攻击无法发生。这意味着安装程序 至关重要 地仔细实施这些安全防护措施,并防止在包安装时被恶意利用。
特别是,安装程序 必须 进行以下检查:
- 符号链接不得指向来自 Wheel 的任何包或命名空间之外
- 符号链接不得悬空(目标在安装时存在)
- 符号链接不得循环,并在达到一定检查深度后停止以避免拒绝服务请求
移除时不要跟随符号链接。
如何教授此内容
一旦这些更改在生态系统中传播开来,最终用户应该透明地体验到 Wheel 中符号链接的好处。如果平台不支持符号链接,安装程序提供清晰的错误消息并解释安装失败的原因非常重要。
对于构建库的人来说,packaging.python.org
上的文档应该描述 Wheel 中符号链接的用例和注意事项(特别是平台支持)。否则,它应该由构建后端透明地处理,就像处理任何普通文件一样。
参考实现
待办事项
被拒绝的想法
到处都只使用 POSIX 符号链接
本 PEP 希望允许 LINKS
用于潜在的未来 PEP 660 可编辑安装。未来的这个 PEP 应该支持 Windows,因此可能需要使用 junctions。
不要在 LINKS
中使用 junctions
Junctions 是在 Windows 上支持文件夹间符号链接的一种受限方式。它们不支持文件。本 PEP 允许使用 junctions,因为用户可能希望只将文件夹链接到不同的位置,并且未来的 PEP 660 实现可能需要依赖此功能。
将符号链接放入 RECORD
元数据文件
虽然这可以做到,但它会使 RECORD
文件变得混乱。此外,最直接的实现会将目标放在记录的末尾。这将使得在行中扫描和直观地查看 Wheel 中存在哪些符号链接变得更加困难。
库维护者应使用 Python 来定位库
使用 Python 定位库会容易得多。然而,有些库,例如 libtorch
,被扩展模块使用,并且本身需要加载依赖项。一些编译库无法使用 Python 找到其加载器依赖项。
包含对硬链接的支持
本 PEP 未指定任何关于硬链接的行为。这是有意为之。这留待未来的 PEP 扩展。
未解决的问题
PEP 660 和延迟可编辑安装支持
本 PEP 将 PEP 660 可编辑安装机制的规范和实现留作未解决的问题,待未来的 PEP 处理;是否应在本 PEP 中指定?
安全
本 PEP 需要审查以确保它不会导致新的安全漏洞。我们是否应该对符号链接的来源或目标施加其他限制以保护用户?
允许包间符号链接
这对于希望在 Wheel 之间分片依赖项(例如大型库)但将其在主父 Wheel 中可用的项目可能很有用。
LINKS
的格式
目前格式是从 RECORD
派生的,但也许存在更好的格式。
先前的讨论
https://discuss.python.org/t/symbolic-links-in-wheels/1945/25
版权
本文档置于公共领域或 CC0-1.0-Universal 许可证下,以更宽松者为准。
来源:https://github.com/python/peps/blob/main/peps/pep-0778.rst