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 (拉取请求)
构建后端
被拒绝的想法
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