PEP 660 – 基于 pyproject.toml 的构建(基于 wheel)的可编辑安装
- 作者:
- Daniel Holth <dholth at gmail.com>,Stéphane Bidoul <stephane.bidoul at gmail.com>
- 赞助商:
- Paul Moore <p.f.moore at gmail.com>
- 讨论邮件列表:
- Discourse 帖子
- 状态:
- 最终
- 类型:
- 标准跟踪
- 主题:
- 打包
- 创建:
- 2021年3月30日
- 历史记录:
- 决议:
- Discourse 帖子
摘要
本文档描述了一种 PEP 517 风格的方法,用于以可编辑模式安装包。
动机
Python 程序员希望能够开发软件包,而无需将其安装(即复制)到 site-packages
中,例如,通过在源代码库的检出副本中工作。
虽然可以通过将相关的源目录添加到 PYTHONPATH
来实现,但 setuptools
提供了 setup.py develop
机制,使该过程更容易,并且还安装依赖项和入口点,例如控制台脚本。 pip
通过其 pip install --editable
选项公开了此机制。
以这样的方式安装项目,即被导入的 Python 代码保留在源目录中,被称为可编辑安装模式。
现在 PEP 517 提供了一种创建 setuptools 替代方案的机制,并将安装前端与构建后端分离,我们需要一种新的机制来以可编辑模式安装软件包。
基本原理
PEP 517 将“可编辑安装”推迟,这意味着非 setup.py
发行版缺少该功能。保留这些发行版的 editable
安装的唯一方法是提供兼容的 setup.py develop
实现。通过定义一个可编辑的钩子,其他构建前端获得了与 setup.py
的同等功能。
术语和目标
可编辑安装模式意味着正在安装的项目的源代码在本地目录中可用。
一旦项目以可编辑模式安装,用户期望对本地源树中的项目Python代码的更改生效,而无需新的安装步骤。
某些类型的更改,例如添加或修改入口点,或添加新的依赖项,需要新的安装步骤才能生效。这些更改通常在构建后端配置文件(例如 pyproject.toml
)中进行,因此与用户普遍期望的一致,即Python源代码是从源树中导入的。
修改非 Python 源代码(例如 C 扩展模块)显然需要编译和/或安装步骤才能生效。要执行的确切步骤将仍然特定于所使用的构建后端。
当项目以可编辑模式安装时,用户期望安装的行为与常规安装相同。特别是,代码必须可被其他代码导入,并且元数据必须可用于标准机制,例如 importlib.metadata
。
根据构建后端实现此规范的方式,可能会出现一些细微的差异,例如源树中存在其他文件,而这些文件不会成为常规安装的一部分。鼓励构建后端记录此类潜在差异。
机制
此 PEP 为 PEP 517 后端接口添加了三个可选钩子。这些钩子用于构建一个 wheel,该 wheel 在安装时允许从其源文件夹导入该发行版。
build_editable
def build_editable(wheel_directory, config_settings=None, metadata_directory=None):
...
必须构建一个 .whl
文件,并将其放置在指定的 wheel_directory
中。它必须返回其创建的 .whl 文件的基文件名(而不是完整路径),作为 Unicode 字符串。
可以作为副作用对发行版进行就地构建,以便任何扩展模块或其他构建工件都已准备好使用。
.whl 文件必须符合 Wheel 二进制文件格式规范(PEP 427)。特别是,它必须包含一个符合规范的 .dist-info 目录。元数据必须与 build_wheel
或 prepare_metadata_for_build_wheel
生成的元数据相同,除了 Requires-Dist
可能略有不同,如下所述。
构建后端必须生成具有与 build_wheel
钩子生成的 wheel 相同的依赖项(Requires-Dist
元数据)的 wheel,但它们可以添加其可编辑机制在运行时正常工作所需的依赖项(例如 editables)。
“可编辑”wheel 的文件名也需要符合 PEP 427。它不需要使用与 build_wheel
相同的标签,但必须标记为与系统兼容。
如果构建前端之前已调用 prepare_metadata_for_build_editable
并且依赖于此调用的结果 wheel 具有与早期调用匹配的元数据,则它应将创建的 .dist-info
目录的路径作为 metadata_directory
参数提供。如果提供了此参数,则 build_editable
必须生成具有相同元数据的 wheel。构建前端传入的目录必须与 prepare_metadata_for_build_editable
创建的目录相同,包括它创建的任何无法识别的文件。
“可编辑”wheel 使用 wheel 格式不是用于分发,而是作为构建系统和前端之间的短暂通信。这避免了构建后端直接安装任何内容。此 wheel 不得公开给最终用户,也不得缓存或分发。
get_requires_for_build_editable
def get_requires_for_build_editable(config_settings=None):
...
此钩子必须返回一个额外的字符串列表,其中包含 PEP 508 依赖项规范,这些规范超出 pyproject.toml
文件中指定的规范,以便在调用 build_editable
钩子时安装。
如果未定义,则默认实现等效于 return []
。
prepare_metadata_for_build_editable
def prepare_metadata_for_build_editable(metadata_directory, config_settings=None):
...
必须在指定的 metadata_directory
中创建包含 wheel 元数据的 .dist-info
目录(即,创建类似 {metadata_directory}/{package}-{version}.dist-info/
的目录)。此目录必须是 wheel 规范中定义的有效 .dist-info
目录,但它不必包含 RECORD
或签名。钩子也可以在此目录内创建其他文件,构建前端必须保留但忽略此类文件;这里的目的是,在元数据依赖于构建时决策的情况下,构建后端可能需要以某种方便的格式记录这些决策,以便在实际的 wheel 构建步骤中重复使用。
它必须返回其创建的 .dist-info
目录的基文件名(而不是完整路径),作为 Unicode 字符串。
如果构建前端需要此信息且未定义该方法,则它应调用 build_editable
并直接查看结果元数据。
放入 wheel 中的内容
构建后端必须使用将在安装后导致可编辑安装的文件填充生成的 wheel。构建后端可以使用不同的技术来实现可编辑安装的目标。本节提供示例,但没有规范性。
- 构建后端可以选择在
.whl
文件的根目录中放置一个.pth
文件,其中包含源树的根目录。这种方法很简单,但不是很精确,尽管它可能被认为足够好(尤其是在使用src
布局时),并且类似于setup.py develop
当前所做的工作。 - editables 库展示了如何构建提供高质量可编辑安装的代理模块。它接受要包含和隐藏的模块列表。当导入时,这些代理模块会用来自源树的代码替换自身。基于路径的方法使路径下的所有脚本都可导入,通常包括项目的自己的
setup.py
和在正常安装中不会包含的其他脚本。代理策略可以实现比基于路径的方法更高的保真度。 - 符号链接是实现可编辑安装的另一种有用机制。由于在撰写本文档时,
wheel
规范不支持符号链接,因此无法直接用于在目标环境中设置符号链接。但是,后端可以在源代码树的某个build
目录中创建符号链接结构,并通过“可编辑”wheel中的.pth
文件将该目录添加到python路径。如果以这种方式链接的一些文件依赖于python实现或版本、ABI或平台,则必须根据兼容性标签在不同的目录中生成链接结构,以便可以在多个环境中以可编辑模式安装相同的项目树。
前端要求
前端必须以与常规wheel相同的方式安装“可编辑”wheel。这也意味着可编辑文件的卸载不需要任何特殊处理。
前端必须在已安装发行版的.dist-info
目录中创建一个direct_url.json
文件,以符合PEP 610。 url
值必须是指向项目目录(即包含pyproject.toml
的目录)的file://
URL,并且dir_info
值必须为{'editable': true}
。
前端必须在包含pyproject.toml
文件中指定的引导需求的环境中执行get_requires_for_build_editable
钩子。
前端必须在包含来自pyproject.toml
的引导需求以及get_requires_for_build_editable
钩子指定的引导需求的环境中执行prepare_metadata_for_build_editable
和build_editable
钩子。
前端不得将从build_editable
获得的wheel公开给最终用户。wheel必须在安装后丢弃,并且不得缓存或分发。
限制
关于wheel的.data
目录,本PEP侧重于使purelib
和platlib
类别(安装到site-packages)“可编辑”。它没有为其他类别(如headers
、data
和scripts
)提供特殊规定。鼓励包作者使用console_scripts
,使他们的scripts
成为围绕库功能的微型包装器,或在开发过程中从源代码检出管理这些脚本。
原型
在撰写本PEP时,各种前端和后端中提供了几个原型实现。我们提供了以下链接来说明可能的方法。
前端
- pip (pull request)
构建后端
- enscons (pull request 1,pull request 2)
- flit (pull request)
- hatchling (sdist)
- pdm (pull request)
- setuptools (setuptools_pep660 repository)
被拒绝的想法
editable
本地版本标识符
关于构建后端附加或修改本地版本标识符以包含editable
字符串的想法已被拒绝,因为它无法满足包含本地版本标识符的==
版本说明符。换句话说,版本1.0+local.editable
无法满足版本pkg==1.0+local
。
虚拟 wheel
在PEP 662中提出了另一种方法,其中构建后端返回从源文件和目录到已安装布局的映射。然后,安装程序前端负责通过其认为适合其用户的任何方式实现可编辑安装。
在功能方面,这两个提案都提供了核心“可编辑”功能。
关键区别在于PEP 662将如何实现可编辑安装的决定权留给前端,而使用本PEP,则必须由后端做出选择。这两种方法原则上都可以为给定项目提供多种可编辑安装方法,并让开发人员在安装时选择一种。
在撰写本PEP时,很明显,社区对可编辑安装有广泛的理论和实践期望。现实情况是,唯一一个拥有广泛经验的是通过.pth插入路径(即setup.py develop所做的)。
我们认为PEP 660通过让项目作者选择提供最适合其需求的可编辑机制的后端或实现方法,并测试其是否正常工作,以最可靠的方式解决了当今这些“未知的未知”。由于前端在如何安装“可编辑”wheel方面没有任何自由度,因此如果出现问题,只有一个地方需要调查:构建后端。
对于PEP 662,需要在前端、后端以及可能规范中调查问题。还很有可能,以不同方式实现规范的不同前端将产生行为与项目作者预期不同的安装,从而造成混乱,或者更糟糕的是,项目只能与特定前端或IDE一起使用。
解压后的 wheel
创建了一个原型,它在临时目录中创建了一个解压的wheel,以便由前端复制到目标环境。没有采用这种方法,因为后端很容易创建wheel存档,并且使用wheel作为通信机制更符合PEP 517的理念,因此使前端的工作更简单。
版权
本文档置于公有领域或根据CC0-1.0-Universal许可证,以更宽松者为准。
来源:https://github.com/python/peps/blob/main/peps/pep-0660.rst
上次修改时间:2023-09-09 17:39:29 GMT