Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 增强提案

PEP 639 – 通过改进软件包元数据提升许可证清晰度

作者:
Philippe Ombredanne <pombredanne at nexb.com>,C.A.M. Gerlach <CAM.Gerlach at Gerlach.CAM>,Karolina Surma <karolina.surma at gazeta.pl>
PEP 代表:
Brett Cannon <brett at python.org>
讨论列表:
Discourse 帖子
状态:
草案
类型:
标准跟踪
主题:
打包
创建日期:
2019年8月15日
更新历史:
2019年8月15日2021年12月17日2024年5月10日
决议:
Discourse 消息

目录

草案接受

本 PEP 已 **草案接受**,在 PEP 成为最终版本之前,需要满足以下条件:

  1. 在两个构建后端中实现 PEP。
  2. 在 PyPI 中实现 PEP。

摘要

本 PEP 定义了 Python 项目中许可证文档的规范。

为了实现这一目标,它

这将使许可证声明对于软件包作者更容易创建、最终用户更容易理解以及工具更容易以编程方式处理,从而减少歧义。

这些更改将 核心元数据规范 更新到 2.4 版。

目标

本 PEP 的范围仅限于涵盖用于记录 分发包 许可证的新机制,具体定义

本 PEP 所需的更改旨在最大程度地减少影响并最大程度地提高向后兼容性。

非目标

本 PEP 并不建议任何特定软件包作者选择任何特定许可证。

如果项目决定不使用新字段,则本 PEP 在上传到 PyPI 时不会施加任何其他限制。

本 PEP 也与单个文件的许可证文档无关,尽管这是附录中 调查的主题,也不打算涵盖 源代码分发二进制分发许可证不同 的情况。

动机

软件必须获得许可,以便除创建者以外的任何人可以下载、使用、共享和修改它。如今,在 核心元数据 中有多个字段用于记录许可证,并且每个字段所能表达的内容都存在限制。这经常导致软件包作者和最终用户(包括分发重新打包者)感到困惑。

这引发了许多与许可证相关的讨论和问题,包括 过时且模棱两可的 PyPI 分类器与其他生态系统的许可证互操作性太多令人困惑的许可证元数据选项Wheel 项目对许可证文件的支持有限 以及 缺少精确的许可证元数据

结果,平均而言,Python 软件包的许可证信息比其他常见生态系统更加模糊和缺失。这得到了 ClearlyDefined 项目开源计划 旨在帮助提高其他 FOSS 项目的许可证清晰度)的 统计页面 的支持,该项目涵盖了来自 PyPI、Maven、npm 和 Rubygems 的所有软件包。

当前的许可证分类器可以扩展为包含 SPDX 标识符的完整范围,同时弃用模棱两可的分类器(例如 License :: OSI Approved :: BSD License)。

但是,有许多论点反对这种方法

  • 需要付出巨大的努力来复制 SPDX 许可证列表并保持同步。
  • 这会造成向后兼容性的重大破坏,迫使软件包作者在 PyPI 弃用旧分类器时立即更新到新分类器。
  • 它仅涵盖单一许可证下的软件包;它没有解决供应商依赖项(例如 Setuptools)、提供许可证选择(例如 Packaging)或重新许可、从其他项目改编代码或包含其他许可证下的字体、图像、示例、二进制文件或其他资产的项目。
  • 它要求作者和工具都理解并实现 PyPI 特定的分类器系统。
  • 它没有提供一个明确的指示,表明软件包已采用新系统,并且应该相应地处理。

基本原理

进行了一项调查,以映射 Python 生态系统各种其他打包系统、Linux 发行版、语言生态系统和应用程序 中现有的许可证元数据定义。

调查结果指导了本 PEP 的建议

  • SPDX 和类似 SPDX 的语法是在许多现代软件包系统中最流行的 许可证表达式
  • 大多数自由和开源软件许可证要求软件包作者在其 分发包 中包含其完整文本。

因此,本 PEP 引入了两个新的核心元数据字段

  • License-Expression,它提供了一种使用 SPDX 许可证表达式无歧义地表达软件包许可证的方式。
  • License-File,它提供了一种标准化的方法,在分发软件包时包含许可证的完整文本,并允许使用 核心元数据 的其他工具查找 分发存档 的许可证文件。

此外,本规范建立在 SetuptoolsWheel 项目中现有实践的基础上。本 PEP 当前草案的最新版本已在 Hatch 打包工具中 实现,并且 许可证文件部分 的早期草案已在 Setuptools 中 实现

术语

本文档中的关键字“必须”、“禁止”、“需要”、“应该”、“不应该”、“推荐”、“可能”和“可选”应按 RFC 2119 中所述进行解释。

许可证条款

许可证相关的术语主要来自 SPDX 项目,特别是 许可证标识符许可证表达式

许可证分类器
PyPI Trove 分类器(如 所述 核心元数据 规范中)以 License :: 开头。
许可证表达式
SPDX 表达式
包含一个或多个 SPDX 许可证标识符的有效 SPDX 许可证表达式语法 字符串,用于描述 项目 的许可证及其相互关系。例如:GPL-3.0-or-laterMIT AND (Apache-2.0 OR BSD-2-clause)
许可证标识符
SPDX 标识符
有效的 SPDX 简写许可证标识符,如本 PEP 中 添加 License-Expression 字段 部分所述。这包括所有有效的 SPDX 标识符以及符合 SPDX 规范,第 10.1 条 的自定义 LicenseRef-[idstring] 字符串。例如:MITGPL-3.0-onlyLicenseRef-My-Custom-License
根许可证目录
许可证目录
存储许可证文件的目录,位于 项目源代码树发行版归档文件已安装项目 中。此外,License-File 核心元数据字段 中记录的路径相对于该根目录。对于 项目源代码树源代码发行版,定义为 项目根目录;对于 构建发行版已安装项目,定义为包含 构建元数据 的目录(即 .dist-info/licenses 目录)的子目录 licenses

规范

实施本 PEP 所需的更改包括

  • 核心元数据 的添加,如 规范 中所定义。
  • 对作者提供的 项目源元数据 的添加,如 规范 中所定义。
  • 添加 到源代码发行版 (sdist)、构建发行版 (wheel) 和已安装项目规范中。
  • 工具指南 处理和转换旧版许可证元数据到许可证表达式,以确保结果一致且正确。

请注意,关于错误和警告的指南适用于工具的默认行为;如果用户明确配置它们这样做,例如通过 CLI 标志或配置选项,它们可能会运行得更严格。

SPDX 许可证表达式语法

本 PEP 采用 SPDX 许可证表达式语法,如 SPDX 规范 中所记录,版本 2.2 或更高兼容版本。

许可证表达式可以使用以下 许可证标识符

  • SPDX 许可证列表 版本 3.17 或任何更高兼容版本中发布的任何 SPDX 列出的许可证简写标识符。请注意,SPDX 工作组从不删除任何许可证标识符;相反,他们可能会选择将某个标识符标记为“已弃用”。
  • 自定义 LicenseRef-[idstring] 字符串,其中 [idstring] 是一个包含字母、数字、. 和/或 - 的唯一字符串,用于标识未包含在 SPDX 许可证列表中的许可证。自定义标识符必须遵循 SPDX 规范,给定规范版本的 第 10.1 条

有效 SPDX 表达式的示例

MIT
BSD-3-Clause
MIT AND (Apache-2.0 OR BSD-2-Clause)
MIT OR GPL-2.0-or-later OR (FSFUL AND BSD-2-Clause)
GPL-3.0-only WITH Classpath-Exception-2.0 OR BSD-3-Clause
LicenseRef-Special-License OR CC0-1.0 OR Unlicense
LicenseRef-Proprietary

无效 SPDX 表达式的示例

Use-it-after-midnight
Apache-2.0 OR 2-BSD-Clause
LicenseRef-License with spaces
LicenseRef-License_with_underscores

核心元数据

本节中的错误和警告指南适用于构建和发布工具;当遇到不符合本规范的格式错误的元数据时,面向最终用户的安装工具可能比此处提到的更宽松。

由于它添加了新字段,因此本 PEP 将核心元数据版本更新为 2.4。

添加 License-Expression 字段

可选的 License-Expression 核心元数据字段 指定为包含一个文本字符串,该字符串是一个有效的 SPDX 许可证表达式,如 上面所定义

构建和发布工具**应该**检查 License-Expression 字段是否包含有效的 SPDX 表达式,包括特定许可证标识符的有效性(如 上面所定义)。当发现无效表达式时,工具**可以**停止执行并引发错误。如果工具选择验证 SPDX 表达式,它们也**应该**使用每个 SPDX 许可证标识符的参考大小写以及 ANDORWITH 关键字的大写来存储 License-Expression 字段的大小写规范化版本。如果一个或多个许可证标识符在 SPDX 许可证列表 中被标记为已弃用,则工具**应该**报告警告,发布工具**可以**引发错误。

对于所有包含 License-Expression 字段的新上传的 发行版归档文件Python 包索引 (PyPI) **必须**验证它们是否包含一个有效的、大小写规范化的许可证表达式以及有效的标识符(如 上面所定义),并且**必须**拒绝不符合要求的上传。符合 SPDX 规范的自定义许可证标识符被视为有效。PyPI **可以**拒绝使用已弃用许可证标识符的上传,只要它在上述 SPDX 许可证列表版本中被弃用。

添加 License-File 字段

License-File 是一个可选的 核心元数据字段。每个实例包含与许可证相关的文件的路径的字符串表示形式。该路径位于 项目源代码树 中,相对于 项目根目录。它是一个多用途字段,可以出现零次或多次,并且每个实例列出一个此类文件的路径。此字段下指定的文件可能包括许可证文本、作者/归属信息或需要与包一起分发的其他法律声明。

本 PEP 中所指定,其值也是该文件相对于 根许可证目录 的路径,无论是在 已安装项目 中还是在标准化的 发行版包 类型中。

如果 License-File 列在 源代码发行版构建发行版 的核心元数据中

  • 该文件**必须**包含在 发行版归档文件 中,路径相对于根许可证目录。
  • 该文件**必须**与 项目 一起安装,路径相同。
  • 指定的相对路径**必须**在项目源代码树、源代码发行版 (sdist)、构建发行版(Wheel)和已安装项目之间保持一致。
  • 在根许可证目录内,打包工具**必须**复制源许可证文件相对于项目根目录所在的目录结构。
  • 路径分隔符**必须**是正斜杠字符 (/),并且**不能**使用父目录指示符 (..)。
  • 许可证文件内容**必须**是 UTF-8 编码的文本。

构建工具**可以**,发布工具**应该**在构建发行版的元数据不包含 License-File 条目时发出提示性警告,发布工具**可以**,但构建工具**不能**引发错误。

对于所有在其核心元数据中包含一个或多个 License-File 字段并在其 Metadata-Version 中声明 2.4 或更高版本的新上传的 发行版归档文件,PyPI **应该**验证所有指定的文件是否都存在于该 发行版归档文件 中,并且**必须**拒绝未通过验证的上传。

弃用 License 字段

传统的非结构化文本 License 核心元数据字段 已被弃用,并由新的 License-Expression 字段取代。这两个字段是互斥的。生成核心元数据的工具**不得**同时创建这两个字段。读取核心元数据的工具,当同时遇到这两个字段时,**必须**读取 License-Expression 的值,并且**必须**忽略 License 字段的值。

如果只有 License 字段存在,工具**可以**发出警告,告知用户该字段已弃用,并建议使用 License-Expression 代替。

对于所有新上传的包含 License-Expression 字段的 发行版归档文件Python 包索引 (PyPI) **必须**拒绝任何同时指定 LicenseLicense-Expression 字段的发行版归档文件。

License 字段可能会在未来某个 PEP 中从规范的新版本中移除。

弃用许可证分类器

Classifier 核心元数据字段 (在核心元数据规范中描述) 中使用 许可证分类器 已被弃用,并由更精确的 License-Expression 字段取代。

如果存在 License-Expression 字段,构建工具**可以**在 Classifier 字段中包含一个或多个许可证分类器时引发错误,并且**不得**自行添加此类分类器。

否则,如果该字段包含许可证分类器,工具**可以**发出警告,告知用户此类分类器已弃用,并建议使用 License-Expression 代替。为了与现有的发布和安装流程兼容,除非也提供了 License-Expression,否则许可证分类器的存在**不应**引发错误。

**不得**将新的许可证分类器 添加到 PyPI;需要使用它们的用户**应**改用 License-Expression 字段。许可证分类器可能会在未来某个 PEP 中从规范的新版本中移除。

项目源元数据

本 PEP 指定了对项目源元数据在 pyproject.toml 文件中 [project] 表下的更改。

license 键添加字符串值

pyproject.toml 文件中 [project] 表中的 license 键被定义为包含一个顶级字符串值。它是一个有效的 SPDX 许可证表达式,如本 PEP 中所定义。其值映射到核心元数据中的 License-Expression 字段。

构建工具**应**验证并执行表达式的案例规范化,如 添加 License-Expression 字段 部分所述,并根据指定输出错误或警告。

示例

[project]
license = "MIT"

[project]
license = "MIT AND (Apache-2.0 OR BSD-2-clause)"

[project]
license = "MIT OR GPL-2.0-or-later OR (FSFUL AND BSD-2-Clause)"

[project]
license = "LicenseRef-Proprietary"

添加 license-files

一个新的 license-files 键被添加到 [project] 表中,用于指定项目源代码树中相对于 pyproject.toml 的路径,指向包含许可证和其他法律声明的文件,这些文件将与包一起分发。它对应于核心元数据中的 License-File 字段。

其值是一个字符串数组,**必须**包含有效的 glob 模式,如下所述

  • 字母数字字符、下划线 (_)、连字符 (-) 和点 (.) **必须**逐字匹配。
  • 特殊 glob 字符:*?** 和字符范围:[],其中只包含逐字匹配的字符**必须**被支持。在 [...] 中,连字符表示不区分区域设置的范围(例如 a-z,基于 Unicode 代码点的顺序)。开头或结尾的连字符将逐字匹配。
  • 路径分隔符**必须**是正斜杠字符 (/)。模式相对于包含 pyproject.toml 的目录,因此**不得**使用前导斜杠字符。
  • **不得**使用父目录指示符 (..)。

本规范未涵盖的任何字符或字符序列均无效。项目**不得**使用此类值。使用此字段的工具**应**使用错误拒绝无效值。

工具**必须**假设许可证文件内容是有效的 UTF-8 编码文本,并且**应**验证这一点,如果它不是,则引发错误。

字面路径(例如 LICENSE)被视为有效的 glob,这意味着它们也可以被定义。

构建工具

  • **必须**将每个值视为一个 glob 模式,并且**必须**在模式包含无效的 glob 语法时引发错误。
  • **必须**在所有发行版归档文件中包含列出的模式匹配的所有文件。
  • **必须**在核心元数据中的 License-File 字段下列出每个匹配的文件路径。
  • **必须**在任何单个用户指定的模式至少不匹配一个文件时引发错误。

如果 license-files 键存在且设置为一个空数组的值,则工具**不得**包含任何许可证文件,并且**不得**引发错误。

有效的许可证文件声明示例

[project]
license-files = ["LICEN[CS]E*", "AUTHORS*"]

[project]
license-files = ["licenses/LICENSE.MIT", "licenses/LICENSE.CC0"]

[project]
license-files = ["LICENSE.txt", "licenses/*"]

[project]
license-files = []

无效的许可证文件声明示例

[project]
license-files = ["..\LICENSE.MIT"]

原因:**不得**使用 ..\ 是无效的路径分隔符,**必须**使用 /

[project]
license-files = ["LICEN{CSE*"]

原因:“LICEN{CSE*” 不是有效的 glob。

弃用 license 键表子键

[project] 表中 license 键的表值,包括 textfile 表子键,现在已弃用。如果存在新的 license-files 键,则如果 license 键已定义且其值不是单个顶级字符串,构建工具**必须**引发错误。

如果不存在新的 license-files 键并且 license 表中存在 text 子键,则工具**应**发出警告,告知用户它已弃用,并建议改为使用许可证表达式作为顶级字符串键。

同样,如果不存在新的 license-files 键并且 license 表中存在 file 子键,则工具**应**发出警告,告知用户它已弃用,并建议改为使用 license-files 键。

如果指定的许可证 file 存在于源代码树中,构建工具**应**使用它来填充核心元数据中的 License-File 字段,并且**必须**包含指定的文件,就好像它是在 license-file 字段中指定的一样。如果该文件在指定的路径中不存在,工具**必须**引发以前指定的提示性错误。

license 键的表值可能会在未来某个 PEP 中从规范的新版本中移除。

项目格式中的许可证文件

将对现有规范进行一些补充。

项目源代码树
根据 项目源元数据 部分,声明项目元数据规范 将更新为反映许可证文件路径**必须**相对于项目根目录;即包含 pyproject.toml 的目录(或等效地,其他旧版项目配置,例如 setup.pysetup.cfg 等)。
源代码分发 (sdist)
sdist 规范 将更新为反映,如果 Metadata-Version2.4 或更高版本,则 sdist **必须**包含 PKG-INFOLicense-File 字段 指定的任何许可证文件,这些文件位于相对于 sdist 的相应路径(包含 pyproject.tomlPKG-INFO 核心元数据)。
构建分发 (wheel)

轮子规范》将更新以反映,如果 Metadata-Version2.4 或更高版本,并且指定了一个或多个 License-File 字段,则 .dist-info 目录**必须**包含一个 licenses 子目录,该子目录**必须**包含 METADATA 文件中的 License-File 字段列出的文件,其路径相对于 licenses 目录。
已安装项目
记录已安装项目规范》将更新以反映,如果 Metadata-Version2.4 或更高版本,并且指定了一个或多个 License-File 字段,则 .dist-info 目录**必须**包含一个 licenses 子目录,该子目录**必须**包含 METADATA 文件中的 License-File 字段列出的文件,其路径相对于 licenses 目录,并且安装工具**必须**从轮子里复制此目录中的任何文件。

转换旧版元数据

工具**不得**使用 license.text [project] 键(或等效的特定于工具的格式)、许可证分类器或核心元数据 License 字段的值来填充 license 键的顶级字符串值或核心元数据 License-Expression 字段,而无需告知用户并要求用户明确、肯定地采取操作来选择和确认所需的许可证表达式值,然后再继续。

需要自动将许可证分类器转换为 SPDX 标识符的工具作者可以使用 PEP 作者编写的建议

向后兼容性

pyproject.toml [project] 表中添加新的 License-Expression 核心元数据字段和 license 键的顶级字符串值明确表示支持本 PEP 中的规范。这避免了新工具将许可证表达式误解为自由格式的许可证描述或反之亦然的风险。

旧版已弃用的核心元数据 License 字段、pyproject.toml [project] 表中的 license 键表子键(textfile)以及许可证分类器保留向后兼容性。删除将留给未来的 PEP 和核心元数据规范的新版本。

新的 License-File 核心元数据字段的规范以及在分发版中添加文件旨在与许多打包工具中该字段的现有用法在很大程度上向后兼容。 pyproject.toml[project] 表中的新 license-files 键只有在用户和工具采用它后才会生效。

本 PEP 指定许可证文件应放在 .dist-info 目录的专用 licenses 子目录中。这是新的,并确保遵循本 PEP 的轮子的许可证位置将与通过以前的安装程序特定行为生成的轮子的许可证位置不同。新的元数据版本进一步支持这一点。

这也解决了当前许可证文件在不同位置具有相同名称时意外替换的问题,从而使轮子无法分发而未被注意到。它还避免了与同一目录中的其他元数据文件发生冲突。

这些新增内容将添加到源分发版 (sdist)、构建分发版 (wheel) 和已安装项目规范中。它们记录了其当前规范下允许的行为,并将它们限制在新元数据版本之后。

本 PEP 建议 PyPI 实现对新的 License-ExpressionLicense-File 字段进行验证,这不会影响上传的新旧软件包,除非它们明确选择使用这些新字段并且未能正确遵循规范。因此,这不会对向后兼容性产生影响,并通过确保上传到 PyPI 的所有具有新字段的分发版都符合规范来保证向前兼容性。

安全隐患

本 PEP 没有预见的安全性影响: License-Expression 字段是普通字符串, License-File 字段是文件路径。两者都没有引入任何已知的新安全问题。

如何教授

大多数软件包使用单个许可证,这使得情况变得简单:单个许可证标识符是有效的许可证表达式。

打包工具的用户将通过工具在检测到无效许可证或使用已弃用的 License 字段或许可证分类器时发出的消息来了解其软件包的有效许可证表达式。

如果使用了无效的 License-Expression,用户将无法将其软件包发布到 PyPI,并且错误消息将帮助他们了解需要使用 SPDX 标识符。可以使用错误的许可证元数据生成分发版,但不能将其发布到 PyPI 或任何其他强制执行 License-Expression 有效性的索引服务器上。对于使用现已弃用的 License 字段或许可证分类器的作者,打包工具可能会警告他们并告知他们替换项 License-Expression

工具还可以帮助转换并在许多常见情况下建议许可证表达式。

参考实现

如果工具决定实施规范的这一部分,则需要支持解析和验证 License-Expression 字段中的许可证表达式。工具可以选择在他们自己的方面实现验证(例如,像hatch)或使用可用的 Python 库之一(例如,license-expression)。本 PEP 并不强制使用任何特定的库,并允许工具作者为他们的项目选择最佳的实现。

被拒绝的想法

提出了许多备选方案,并在仔细考虑后被拒绝。包括拒绝理由的完整列表可以在单独页面中找到。

附录

提供辅助文档列表

致谢

  • Alyssa Coghlan
  • Kevin P. Fleming
  • Pradyun Gedam
  • Oleg Grenrus
  • Dustin Ingram
  • Chris Jerdonek
  • Cyril Roelandt
  • Luis Villa
  • Seth M. Larson
  • Ofek Lev

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

上次修改时间:2024-08-29 18:30:14 GMT