PEP 639 – 使用更好的软件包元数据提高许可证清晰度

猫勺猫勺 03-12 150 阅读 0 评论

抽象

PEP 定义了如何在核心元数据中记录许可证的规范,并在新字段中使用 SPDX 标识符的许可证表达式字符串。 这将使许可证声明更简单,更不模棱两可 要创建的包作者,要阅读和理解的最终用户,以及 以编程方式处理的工具。License-Expression

PEP还:

  • 正式指定一个新字段,并定义许可证文件应如何包含在分发中, 正如 Wheel 和 Setuptools 项目已经使用的那样。License-File

  • 弃用旧字段和分类器。Licenselicense ::

  • 添加和弃用相应的键 在表中。pyproject.toml[project]

  • 为作者提供明确的指导,并 转换旧版许可证元数据、添加许可证文件和 验证许可证表达式。

  • 描述一个参考实现, 分析众多潜在的替代方案, 包括详细的例子, 解释用户方案和 调查 Python 打包和其他生态系统中的许可证文档

此 PEP 中的更改会将核心元数据更新到 2.4 版本,修改项目(源)元数据规范, 并对源分配 (SDIST)、构建分配 (Wheel) 和已安装项目标准进行少量添加。

目标

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

  • 指定 SPDX 许可证表达式的方法。

  • 一种在发行版和已安装项目中包含许可证文本的方法。

此 PEP 要求对核心元数据规范的更改已 旨在最大限度地减少影响并最大限度地提高向后兼容性。 此规范建立在记录许可证的现有方法之上,这些方法 已经在流行的工具中使用(例如,为 Wheel 和 Setuptools 项目中已经使用的字段添加对核心元数据的支持)和一些包作者 (例如,在现有字段中存储 SPDX 许可表达式)。License-FileLicense

除了这些建议的更改之外,此 PEP 还包含工具指南 处理和转换这些元数据,面向包作者的教程 涵盖各种常见用例,使用中的详细示例, 以及对 Python 和其他许可证文档的全面调查 语言。

PEP 作者的意图是与工具维护者密切合作,以 实施此处指定的验证和警告建议。

非目标

本 PEP 对任何特定许可证的选择都是中立的 包作者。此 PEP 不对特定许可证提出建议, 并且不需要使用特定的许可证文档约定。

相反,此 PEP 中建议的 SPDX 许可证表达式语法提供了一个 更简单、更富有表现力的机制,可准确记录任何类型的 适用于 Python 包的许可证,无论它是开源的, 自由/自由、专有或此类组合。

此 PEP 在上传到 PyPI,除非项目选择使用新字段。

相反,它旨在记录已经在使用的最佳实践,并对其进行扩展 使用新的正式指定和支持的机制,并提供指导 对于打包工具如何进行过渡并相应地通知用户。

此 PEP 也与项目内部文件中的许可证文档无关, 尽管这是附录中的调查主题,也不打算涵盖来源和 二进制分发包没有相同的许可证。

赋予动机

软件必须获得许可,才能让其创建者以外的任何人 下载、使用、共享和修改它,从而提供准确的许可证信息 对 Python 包用户来说是一件重要的事情。 今天,有多个领域 许可证记录在核心元数据中,并且对哪些内容有限制 可以用它们中的每一个来表达。这通常会导致混乱和缺乏 清晰度,适用于软件包作者和最终用户。

许多软件包作者表达了困难和挫败感,因为 在项目元数据中表达许可的能力有限,而这 给 Linux 和 BSD 发行版重新打包者带来了进一步的麻烦。 这引发了许多与许可证相关的讨论和问题, 包括过时和模棱两可的 PyPI 分类器、与其他生态系统的许可证互操作性、太多令人困惑的许可证元数据选项、对 Wheel 项目中许可证文件的支持有限,以及缺乏清晰、精确和标准化的许可证元数据。

当前的许可证分类器解决了一些常见情况,并且可以 扩展为包括当前 SPDX 标识符的全部范围 同时弃用许多模棱两可的分类器 (包括一些流行和有问题的, 如)。 然而,这需要大量的努力 复制 SPDX 许可证列表并使其保持同步。 此外,它实际上是向后兼容性的硬性突破, 迫使很大一部分软件包作者立即更新到新的 分类器(在大多数情况下,有许多可能的选择,需要密切 检查项目的许可证),当 PyPI 弃用旧的许可证时立即检查。License :: OSI Approved :: BSD License

此外,这仅涵盖完全在单个许可证下的简单软件包; 它没有解决供应商的常见项目的很大一部分 依赖项(例如 Setuptools),提供许可证选择(例如打包) 或重新许可,改编其他项目的代码或包含字体、图像、 示例,二进制文件或其他许可证下的其他资产。它还需要 作者和工具都理解并实现特定于 PyPI 的定制 分类器系统,而不是使用简短,易于添加和标准化 SPDX 标识符在简单文本字段中,随着 大多数其他包装系统,以减轻生态系统的整体负担。 最后,这并不能清楚地表明一个包 已采用新系统,应相应处理。

平均而言,Python 包往往具有更多模棱两可和缺少许可证 比其他常见生态系统(如 npm、Maven 或 宝石)。这得到了 ClearlyDefined 项目的统计页面的支持,该项目是开源促进会孵化的一项努力,旨在提供帮助 提高其他自由和开放源码软件项目的许可清晰度,涵盖所有软件包 来自 PyPI、Maven、npm 和 Rubygems。

理由

对 Python 中使用的现有许可证元数据定义的调查 今天的生态系统在本 PEP 的附录中提供, 以及各种其他包装系统中的许可文件, Linux 发行版、语言、生态系统和应用程序在另一个附录中进行了调查。

调查中有一些要点,这些要点指导了设计 以及本 PEP 的建议:

  • 大多数包格式使用单个字段。License

  • 许多现代包系统使用某种形式的许可证表达式语法来 (可选)将多个许可证标识符组合在一起。 SPDX 和类似 SPDX 的语法是最常用的。

  • SPDX 许可证标识符正在成为引用通用的事实上的方式 许可证无处不在,无论是否使用完整的许可证表达式语法。

  • 多种包格式支持记录许可证表达式和 包含许可证文本的相应文件的路径。最免费和 开源软件许可证要求包作者包含其完整的 分布中的文本。

使用新字段将提供直观的, 结构化和明确的方式来表达许可证 使用明确定义的语法和已知的许可证标识符进行包。 同样,正式指定的字段提供标准化的 确保许可证的全文包含在 在分发时打包,符合法律要求,并允许使用其他工具 核心元数据,用于明确定位发行版的许可证文件。License-ExpressionLicense-File

同时大大简化和改进了当前的 Python 许可证 元数据故事,此规范标准化并建立在 Setuptools 和 Wheel 项目中的现有实践。 此外,该 PEP 当前草案的最新版本已经在流行的 PyPA Hatch 打包工具,以及 许可证文件部分是在 Setuptools 中实现的。

随着时间的流逝,鼓励使用这些字段并弃用模棱两可的字段, 重复和令人困惑的遗留替代方案将有助于 Python 软件 出版商提高了许可的清晰度、准确性和可移植性 实践,使软件包作者、消费者和再发行商受益 一样。

术语

这个 PEP 试图明确定义它使用的术语,因为有些术语已经 多个既定含义(例如,导入与分发包, 轮子格式与轮子项目);是相关的,经常使用 可以互换,但在含义上有关键的区别 (例如 键与核心元数据字段);是现有概念 没有正式术语/定义(例如项目/源元数据与。 分发/构建元数据,构建与发布工具),或者是新概念 此处介绍(例如许可证表达/标识符)。[project]

此 PEP 还使用 PyPA PyPUG 词汇表(专门构建/二进制分发、分发包、项目和源代码分发)和 SPDX 项目(许可证标识符、许可证表达式)中定义的术语。

关键词“MUST”、“MUST NOT”、“REQUIRED”、 “应该”、“不应该”、“推荐”、“可以”和“可选” 本文档中的说明将按照 RFC 2119 中的说明进行解释。

此处列出了完整版本的条款; 相关词()在括号内, 包括简称 ()、子词 () 和常用同义词 就本 PEP 而言 ()。Rel:Short:Sub:Syn:

核心元数据(syn:包元数据,sub:分发元数据)

PyPA 规范和元数据字段集 它定义了描述分发包的关键静态属性 和已安装的项目。

更具体地说,分布元数据是指具体形式 包含在分发存档中的核心元数据 (在 sdist 和轮子中)或已安装的项目 ().PKG-INFOMETADATAMETADATA

核心元数据字段(简称:元数据字段/字段)

单个键值对,或具有相同键的序列,如定义 通过核心元数据规范。 值得注意的是,与表键不同。pyproject.toml[project]

分发包(子:包、分发存档)

(参见 PyPUG) 在这个 PEP 中,包用于指代 Python 项目的可分发形式,而分发更多 特别引用物理分发存档。

许可证分类器

PyPI Trove 分类器(如核心元数据规范中所述) 以 开头,当前用于表示 通过将项目的许可证状态作为 包含在核心元数据中。License ::Classifier

许可证表达式 (Syn: SPDX Expression)

具有有效 SPDX 许可证表达式语法的字符串,包括此处定义的任何 SPDX 许可证标识符,其中描述 项目的许可证以及它们之间的关系。例子:GPL-3.0-or-laterMIT AND (Apache-2.0 OR BSD-2-clause)

许可证标识符(Syn:许可证 ID/SPDX 标识符)

有效的 SPDX 短格式许可证标识符,如本 PEP 的“添加许可证表达式”字段部分所述;简要 这包括所有有效的 SPDX 标识符和 和 字符串。例子:LicenseRef-Public-DomAInLicenseRef-ProprietaryMITGPL-3.0-only

项目(子:项目源代码树、已安装项目)

(参见 PyPUG) 这里,项目源代码树是指 用于开发的项目,而已安装的项目是 project 从 PyPA 指定的发行版安装一次。

项目源元数据(子:项目表元数据、键、子项)

包作者在项目源代码树中定义的核心元数据, 作为文件表中的顶级键, 在表中,或等效于其他 构建工具。[project]pyproject.toml[metadata]setup.cfg

项目表元数据或元数据 具体指前者,由 PyPA 声明项目元数据规范定义,最初在 PEP 621 中指定。 项目表键或非限定键专门指 顶级密钥 (值得注意的是,与核心元数据字段不同), 而子项是指表值键中的二级键。pyproject.toml[project][project][project]

根许可证目录(简称:许可证目录)

许可证文件存储在项目/分发中的目录 以及其路径(记录在核心元数据字段下)相对于的根目录。 此处定义为源代码树和源代码的项目根目录 distributions,以及以该目录命名的子目录 包含已构建发行版和已安装项目的核心元数据(即目录)。License-Filelicenses.dist-info/licenses

工具(子:打包工具、构建工具、安装工具、发布工具)

由用户执行或自动执行的程序、脚本或服务 力求符合本 PEP 中定义的规范。

打包工具是指用于构建、发布、 安装,或以其他方式直接与 Python 包交互。

构建工具是用于生成源或构建的打包工具 从项目源代码树或 SDIST 分发(直接调用时) 因此(而不是面向最终用户的安装工具)。 示例:Wheel 项目、PEP 517 后端 via 或其他 面向 package-developers 的前端,直接调用。buildsetup.py

安装工具是用于安装源代码或构建的打包工具 在目标环境中的分布。示例包括 PyPA pip 和项目。installer

发布工具是用于上传分发的打包工具 存档到包索引,例如 Twine for PyPI。

Wheel(短:wheel,Rel:wheel format,Wheel project)

在这里,wheel 是 PEP 427 中引入并由 PyPA 指定的标准构建分发格式,将在 小写,而 Wheel 项目,其引用 实现,在标题大小写中将与 Wheel 一起称为 Wheel。

规范

实施改进的许可证处理所需的更改 此 PEP 包括分发包元数据中的那些, 如核心元数据规范和作者提供的项目源元数据中所定义, 在项目源元数据规范中定义(最初在 PEP 621 中引入)。

此外,对 源配电 (SDIST)、建成配电 (WHEEL) 和安装项目 规范将有助于记录和澄清已经允许的, 现在正式规范了这些方面的行为。 最后,为处理旧许可证元数据并将其转换为许可证的工具建立了指南 表达式,以确保结果一致、正确和明确。

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

核心元数据

PyPA 核心元数据规范定义了名称 以及 Python 分发包和已安装的项目。

此 PEP 添加字段,添加字段,弃用字段, 并弃用许可证分类器 在现场。License-ExpressionLicense-FileLicenseClassifier

本部分中的错误和警告指南适用于生成和 出版工具;面向最终用户的安装工具可能比 遇到格式错误的元数据时在此处提及 不符合此规范。

在添加新字段时,此 PEP 会将核心元数据更新到版本 2.4。

添加字段

指定可选字段以包含文本字符串 这是此处定义的有效 SPDX 许可证表达式。License-Expression

如果此字段是 缺失,并可能引发错误。生成工具可能会发出类似的警告, 但不得引发错误。

许可证表达式是使用 SPDX 许可证表达式语法的字符串 记录在 SPDX 规范中,或者 版本 2.2 或更高版本的兼容版本。

当在该领域使用并作为专业化时 SPDX 许可证表达式定义,许可证表达式可以使用 以下许可证标识符:License-Expression

在 SPDX 许可证列表 3.17 版或更高版本中发布的任何 SPDX 列出的许可证短格式标识符 版本。请注意,SPDX 工作组从不删除任何许可证 标识符;相反,他们可能会选择将标识符标记为“已弃用”。

和 字符串,以 标识未包含在 SPDX 许可证列表中的许可证。LicenseRef-Public-DomainLicenseRef-Proprietary

处理字段以确定它是否包含 有效的许可证表达式、构建和发布工具:License-Expression

如果出现以下情况,应停止执行并引发错误:

该字段不包含有效的许可证表达式

一个或多个许可证标识符无效 (定义见上文)

应报告信息警告,发布工具可能会引发 错误,如果一个或多个许可证标识符已在 SPDX 许可证列表。

必须存储字段的案例规范化版本 使用每个 SPDX 许可证标识符的参考用例,以及 和 关键字的大写。License-ExpressionANDORWITH

应报告信息警告,如果出现以下情况,可能会引发错误 规范化过程会导致对字段内容的更改。License-Expression

对于包含字段的所有新上传发行版,Python 包索引 (PyPI) 必须 验证它是否包含有效的、大小写规范化的许可证表达式 有效标识符(定义如此处),并且必须拒绝不这样做的上传。 PyPI 可能会因使用已弃用的许可证标识符而拒绝上传, 只要它在上述 SPDX 许可证列表中被弃用 版本。License-Expression

添加字段

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

根据此 PEP 的规定,其值 也是该文件相对于根许可证目录的路径 已安装的项目和标准化的分发包类型。 在其他旧版、非标准或新的分发包格式中,以及 访问和存储核心元数据的机制,值可以对应 到相对于格式定义的根许可证目录的许可证文件路径。 或者,可以将其视为访问 通过其他方式许可文件内容,如格式指定。

如果 a 列在源代码或构建发行版的核心中 metadata,则该文件必须包含在指定路径的分发中 相对于根许可证目录,并且必须与 在同一相对路径上的分布。License-File

指定的相对路径在项目源代码树之间必须一致, 源代码发行版 (SDIST)、内置发行版 (wheels) 和安装 项目。因此,在根许可证目录中,打包工具 必须重现目录结构,在该结构下 源许可证文件位于相对于项目根目录的位置。

路径分隔符必须是正斜杠字符 (), 和父目录指示符 () 不得使用。 许可证文件内容必须是 UTF-8 编码的文本。/..

生成工具 MAY 和发布工具应生成信息性警告 如果构建的发行版的元数据不包含任何条目, 发布工具可以,但生成工具不得引发错误。License-File

对于包含一个或多个字段并声明 of 或 更高,则 PyPI 应验证指定的文件是否存在于所有文件中 上传的分发,并且必须拒绝未验证的上传。License-FileMetadata-Version2.4

Deprecate 字段

旧版非结构化文本字段已弃用,取而代之的是 新字段。生成和发布工具必须提高 如果这两个字段都存在并且它们的值不相同,则为错误, 包括大写,不包括前导和尾随空格。LicenseLicense-Expression

如果仅存在该字段,则此类工具应发出警告 通知用户它已被弃用并改为推荐。LicenseLicense-Expression

对于包含字段的所有新上传的发行版,Python 包索引 (PyPI) 必须 拒绝任何指定字段且其文本不是 与本节中定义的 相同。License-ExpressionLicenseLicense-Expression

与许可证分类器一起,该字段可以从 未来 PEP 中的新版本规范。License

弃用许可证分类器

在现场使用许可证分类器 (在核心元数据规范中描述) 已弃用,并替换为更精确的字段。ClassifierLicense-Expression

如果该字段存在,则构建工具应和 如果一个或多个许可证分类器,发布工具必须引发错误 包含在字段中,并且不得添加 这样的分类器本身。License-ExpressionClassifier

否则,如果此字段包含许可证分类器,则构建工具可以 发布工具应发出警告,通知用户此类分类器 已弃用,并改为推荐。 为了与现有的发布和安装过程兼容, 许可证分类器的存在不应引发错误,除非还提供了。License-ExpressionLicense-Expression

对于包含字段的所有新上传的发行版,Python 包索引 (PyPI) 必须 拒绝任何同时指定任何许可证分类器的分类器。License-Expression

不得将新的许可证分类器添加到 PyPI; 需要它们的用户应改用该字段。 与字段一起,许可证分类器可以从 未来 PEP 中的新版本规范。License-ExpressionLicense

项目源元数据

正如最初在 PEP 621 中引入的那样,PyPA 声明项目元数据规范定义了如何声明项目的源代码 文件中表下的元数据 构建用于使用和输出分发核心元数据的工具。[project]pyproject.toml

此 PEP 为密钥添加顶级字符串值,添加新密钥 并弃用键的表值 以及其相应的表子项和 .licenselicense-fileslicensetextfile

向键添加字符串值

定义顶级字符串值 对于表中的键, 它被指定为有效的 SPDX 许可证表达式, 如前所述。 其值映射到核心元数据中的字段。license[project]License-Expression

构建工具应验证表达式,如“添加许可证-表达式”字段部分所述, 输出指定的错误或警告。 生成核心元数据时,工具必须执行大小写规范化。

如果密钥的顶级字符串值存在且有效, 出于向后兼容性的目的 tools 可以回填核心元数据字段 替换为键的规范化值。licenseLicenselicense

添加密钥

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

它的值是一个表,如果存在,则必须包含两个可选表之一, 互斥子项和 ;如果两者都指定, 工具必须引发错误。两者都是字符串数组;子项 包含逐字文件路径和子项有效的 glob 模式, 它必须可由 Python 标准库。pathsglobspathsglobsglob

注意:为了避免歧义、混淆和(根据 PEP 20,Python 的禅宗) “不止一种(显而易见的)方法可以做到这一点”,允许使用扁平的字符串数组 因为密钥的值暂时被省略了。license-files

路径分隔符必须是正斜杠字符 (), 和父目录指示符 () 不得使用。 工具必须假定许可证文件内容是有效的 UTF-8 编码文本, 并且应该验证这一点,如果不是,则引发错误。/..

如果子项是非空数组,则生成工具:paths

必须将每个值视为逐字的文本文件路径,并且 不得将它们视为球形模式。

必须包括所有分发存档中列出的每个文件。

不得与显式许可证文件以外的任何其他许可证文件匹配 由用户在子项下静态指定。paths

必须在核心元数据中的字段下列出每个文件路径。License-File

如果一个或多个路径与有效文件不对应,则必须引发错误 在项目源中,可以复制到分发存档中。

如果子项是非空数组,则生成工具:globs

必须将每个值视为 glob 模式,并且如果 pattern 包含无效的 glob 语法。

必须包含与至少一个列出的模式匹配的所有文件 分发档案。

可以排除与 glob 模式匹配的文件,这些模式可以明确 确定为备份、临时、隐藏、操作系统生成或 VCS 忽略。

必须在 核心元数据。License-File

应发出警告,如果没有匹配的文件,则可能会引发错误。

如果有任何用户指定的模式,则可以发出警告 与至少一个文件不匹配。

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

如果该键不存在且未显式标记为 ,则工具必须采用以下默认值:license-filesdynamic

license-files.globs = ["LICEN[CS]E*", "COPYING*", "NOTICE*", "AUTHORS*"]

在这种情况下,如果没有匹配的许可证文件,工具可能会发出警告。 但不得引发错误。

如果密钥标记为(且不存在), 保持与当前工具的行为一致,并帮助确保包 他们创建的是合法分发的,构建工具应默认为 至少包括与上述模式匹配的许可证文件,除非 用户已明确指定了自己的。license-filesdynamic

弃用键表子项

表中键的表值, 包括 和 表子项,现已弃用。 如果存在新密钥, 如果定义了密钥,则生成工具必须引发错误 并且具有单个顶级字符串以外的值。license[project]textfilelicense-fileslicense

如果新密钥不存在 并且子项存在于表中, 工具应发出警告,通知用户它已被弃用 并建议将许可证表达式作为顶级字符串键。license-filestextlicense

同样,如果新密钥不存在 并且子项存在于表中, 工具应发出警告,通知用户它已被弃用并推荐 而是钥匙。license-filesfilelicenselicense-files

如果源代码树中存在指定的许可证, 构建工具应该用它来填充字段 ,并且必须包含指定的文件 就好像它是在字段中指定的一样。 如果指定路径中不存在该文件, 工具必须引发前面指定的信息性错误。 但是,工具还必须假定密钥的指定默认值,并且还包括: 除了在子项下指定的许可证文件外, 与指定模式列表匹配的任何许可证文件。fileLicense-Filelicense-file.pathslicense-fileslicense.file

键的表值可能会被删除 来自未来 PEP 中规范的新版本。license

项目格式的许可证文件

将对相关的现有规范进行一些小的补充 记录、标准化和澄清当前已经支持的内容, 允许和实现的行为,以及明确提及根 许可证目录 许可证文件位于 for 中并相对于 每种格式,根据“添加许可证文件”字段部分。

项目源树

如“项目源元数据”部分所述,“声明项目元数据”规范将更新,以反映许可证文件路径必须相对于 项目根目录;即包含(或等效的其他遗留项目配置)的目录, 例如,,等)。pyproject.tomlsetup.pysetup.cfg

源代码分发 (sdist)

sdist 规范将更新,以反映 对于 is 或更大,sdist 必须包含 在 AT 中指定的许可证文件 相对于 SDIST 顶级目录的相应路径 (包含 和 核心元数据)。Metadata-Version2.4License-FilePKG-INFOpyproject.tomlPKG-INFO

内置分配(轮子)

车轮规格将更新,以反映以下情况 is 或更大,并且指定了一个或多个字段,则目录必须 包含一个子目录,该子目录必须包含列出的文件 在文件中的字段中,它们各自 相对于目录的路径。Metadata-Version2.4License-File.dist-infolicensesLicense-FileMETADATAlicenses

已安装的项目

Recording Installed Projects 规范将是 更新以反映如果 is 或更大 并且指定了一个或多个字段,则该目录必须包含一个子目录,该子目录必须包含 文件中的字段中列出的文件 在它们相对于目录的各自路径中, 并且此目录中的任何文件都必须从轮子复制 通过安装工具。Metadata-Version2.4License-File.dist-infolicensesLicense-FileMETADATAlicenses

转换旧元数据

工具不得使用密钥的内容 (或等效的工具特定格式), 许可证分类器或核心元数据字段的值 填充键的顶级字符串值 或核心元数据字段 在不通知用户的情况下,要求用户采取明确的、肯定的行动 在继续操作之前选择并确认所需的许可证表达式值。license.text[project]LicenselicenseLicense-Expression

大多数单一许可证分类器(即下面未提及的所有分类器) 映射到单个有效的 SPDX 许可证标识符, 允许工具推断它们对应的 SPDX 许可证标识符, 既用于分析和审核包, 并提供半自动填充密钥的机制 或字段 遵循上述规范。licenseLicense-Expression

一些旧版许可证分类器打算指定特定的许可证, 但不要具体说明特定的版本或变体,导致其术语、兼容性和可接受性严重模糊。 当使用这些分类器之一时,工具不得尝试自动推断 在没有肯定的用户操作的情况下:License-Expression

  • License :: OSI Approved :: Academic Free License (AFL)

  • License :: OSI Approved :: Apache Software License

  • License :: OSI Approved :: Apple Public Source License

  • License :: OSI Approved :: Artistic License

  • License :: OSI Approved :: BSD License

  • License :: OSI Approved :: GNU Affero General Public License v3

  • License :: OSI Approved :: GNU Free Documentation License (FDL)

  • License :: OSI Approved :: GNU General Public License (GPL)

  • License :: OSI Approved :: GNU General Public License v2 (GPLv2)

  • License :: OSI Approved :: GNU General Public License v3 (GPLv3)

  • License :: OSI Approved :: GNU Lesser General Public License v2 (LGPLv2)

  • License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)

  • License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)

  • License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)

这些分类器与其可能的特定分类器的全面映射 标识符由达斯汀·英格拉姆(Dustin Ingram)组装,其中工具 可以用作标识符选择选项的参考,以提供给用户 提示用户显式选择许可证标识符时 他们打算他们的项目。

注意

几个额外的分类器,即“或更高版本”的变体 AGPLv3、GPLv2、GPLv3 和 LGPLv3 也列在上述 映射,但明确映射到各自的许可证, 所以这里没有列出。 但是,上面包含了 LGPLv2,因为它可能模棱两可 请参阅该许可证的不同 v2.0 或 v2.1 变体。

此外,对于各种特殊情况,以下映射是 就本规范而言,被认为是规范和规范性的:

分类器可以映射到泛型 . 如果工具这样做,他们应该发布一个鼓励的信息警告 使用更明确和合法的许可证标识符, 例如用于 CC0 1.0 许可证的那些 (), 无牌 (), 或麻省理工学院许可证 (), 由于与“公有领域”一词相关的含义是彻底的 取决于所涉及的具体法律管辖权, 其中一些完全缺乏这个概念。 或者,工具可以选择将这些分类器视为模棱两可的分类器。License :: Public DomainLicense-Expression: LicenseRef-Public-DomainCC0-1.0UnlicenseMIT

  • License :: Free For Educational Use

  • License :: Free For Home Use

  • License :: Free for non-commercial use

  • License :: Freely Distributable

  • License :: Free To Use But Restricted

  • License :: Freeware

  • License :: Other/Proprietary License

MAY 映射到泛型 , 但是,如果工具这样做,它们必须发出醒目的、信息丰富的警告。 或者,工具可以选择将这些分类器视为模棱两可的分类器。License-Expression: LicenseRef-Proprietary

泛型和模棱两可的分类器,并且不映射到任何许可证表达式, 因此,工具应该将它们视为模棱两可的,或者如果不是,则必须忽略它们。License :: OSI ApprovedLicense :: DFSG approved

分类器没有映射到 SPDX 许可证 标识符,截至 2022 年 7 月 9 日,没有 PyPI 包使用它们。License :: GUST Font License 1.0License :: GUST Font License 2006-09-30

当使用多个许可证分类器时,它们之间的关系是模棱两可的, 并且通常无法确定是否所有许可证都适用,或者是否适用 在许可证中有一个选择, 在这种情况下,工具不得自动推断许可证表达式, 除非一个许可证分类器是另一个许可证分类器的父级, 即子项包含父项的所有描述组件, 在这种情况下,工具可能会忽略父分类器 但在这样做时应发出信息性警告。::

向后兼容性

添加新的专用核心元数据字段 以及为此目的保留的密钥的顶级字符串值 在表中 明确表示支持此 PEP 中的规范。 这避免了新工具的风险 将许可证表达式误解为自由格式的许可证说明 反之亦然,当且仅当用户肯定时才会引发错误 升级到最新的元数据版本并添加新的字段/键。License-Expressionlicensepyproject.toml[project]

旧版核心元数据字段 和键表子项 ( 和 ) 在表中 将与许可证分类器一起弃用, 保持向后兼容性,同时温和地为用户做好准备 将来删除。这种移除将在一个适当的过渡期之后进行,并且 留给未来的 PEP 和新版本的核心元数据规范。Licenselicensetextfilepyproject.toml[project]

正式指定新的核心元数据字段和 在发行版中包含列出的文件只是编纂和 完善了流行包装工具中的现有做法,包括 Wheel 和 Setuptools 项目,并且设计为在很大程度上向后兼容 以及他们对该字段的现有使用。同样,表中的新键以静态方式指定要包含的文件, 以及默认行为,并允许其他工具使用它们, 而只有在用户和工具明确采用它时才会生效。License-Filelicense-files[project]pyproject.toml

由于要求许可证文件不能扁平化为 指定它们应放置在专用的子目录中, 在此更改之后生产的车轮将具有不同的位置 与通过先前未指定生成的许可证相关的许可证, 特定于安装程序的行为,但直到此 PEP 才有办法 发现这些文件或以编程方式访问它们,这将 被新的元数据版本进一步区分,没有任何预见 这会带来一个实际问题的机制。.dist-infolicenses

此外,这还解决了与当前 临时行为,即许可证文件在有 与不同路径上的其他人同名,在不知不觉中渲染了轮子 不可分发,并且与 相同的目录。以其他方式正式指定实际上会阻止 full 向前兼容其他标准或安装程序指定的文件 和目录添加到 ,因为它们也可能与 冲突 现有许可证的名称。.dist-info

虽然将对源代码分发 (sdist) 进行少量添加, 内置配电(轮)和安装项目规范,所有这些 只是明确地记录、澄清和正式指定行为 允许在他们当前的各自规范下,并且已经实施 在实践中,并将它们置于新事物的明确存在背后 元数据版本和新字段。特别是,sdist 可能包含 遵循项目源代码树布局的任意文件,并正式 提及这些必须包括元数据中列出的许可证文件 仅记录和编纂现有的 Setuptools 实践。同样,任意的 Wheels 目录中允许使用特定于安装程序的文件 并复制到已安装的项目中,这个 PEP 再次正式澄清 并标准化已经完成的工作。.dist-info

最后,虽然这个 PEP 确实建议 PyPI 实现对 new 和 字段的验证,但这对 现有软件包,也不会对上传的任何新发行版产生任何影响,除非它们 明确选择选择使用这些新字段,而不 遵循规范中的要求。因此,这没有 向后兼容性影响,实际上确保向前兼容性 通过确保将所有发行版上传到 PyPI 以及新的 字段有效且符合规范。License-ExpressionLicense-File

安全隐患

此 PEP 没有可预见的安全隐患:字段是纯字符串,字段是文件路径。 两者都不会引入任何已知的新安全问题。License-ExpressionLicense-File

如何教这个

简单的情况很简单:单个许可证标识符就是一个有效的许可证 表达式,并且大多数软件包都使用单个许可证。

计划教包装工具的用户如何表达他们的包装 具有有效许可证表达式的许可证是要有工具问题信息 当检测到无效的许可证表达式时,或者使用已弃用的字段或许可证分类器时,会发送消息。License

如果使用了无效的 SPDX 标识符,则立即提供描述性错误消息,这将有助于用户了解他们需要在 这个字段,如果他们犯了错误,就抓住他们。 对于仍在使用现已弃用、不太精确且冗余较多的字段或许可证分类器的作者,打包工具将发出警告 他们并告知他们现代替代品,. 最后,对于可能已经忘记或不知道他们需要这样做的用户, 发布工具将温和地引导他们在其项目源元数据中包含 和 。License-ExpressionLicenseLicense-Expressionlicenselicense-files

工具还可以帮助进行转换,并在 许多(如果不是最常见的)情况:

将许可证分类器映射到 SPDX 标识符一节提供了 工具作者提供有关如何建议生成的许可证表达式的指南 来自旧版分类器。

工具还可以推断并建议如何更新 项目源元数据中的现有值 并将其转换为许可证表达式, 正如本 PEP 中所指定的那样。 例如,工具可能会建议将键中的值转换为(或工具特定格式的等效值) 添加到键的顶级字符串值(或等效项)。 同样,工具可以建议从 of (这不是有效的许可证表达式) 进行转换 如本 PEP 中所定义) 更改为 of (使用 SPDX 许可证标识符的等效有效许可证表达式)。LicenseMITlicense.text[project]licenseLicenseApache2License-ExpressionApache-2.0

参考实现

工具将需要支持在现场解析和验证许可证表达式。License-Expression

license-expression 库是一个参考 Python 处理许可证表达式的实现,包括解析、 格式化和验证,使用灵活的许可证符号列表 (包括 SPDX 许可证 ID 和此处包含的任何额外标识符)。 它在 Apache-2.0 下获得许可,并且已经在多个项目中使用, 包括 SPDX Python 工具, ScanCode 工具包和欧洲自由软件基金会 (FSFE) 重用项目。

被拒绝的想法

核心元数据字段

结构、内容和弃用的潜在替代方案 此 PEP 中指定的核心元数据字段。

重用字段

经过初步讨论,此的早期版本 PEP 建议重用现有字段,哪些工具将 尝试解析为 SPDX 许可证表达式,并回退到自由文本。 最初,这只会引起警告(甚至无声传递), 但最终会被现代工具视为错误。License

这提供了更大的向后兼容性的潜在好处, 让社区在利用 SPDX 许可表达式的同时轻松使用 已经有它们的包(有意或无意), 并避免添加另一个与许可证相关的字段。

然而,经过大量讨论,达成了共识,即 专用字段是首选的总体方法。 此字段的存在是一个明确的信号,表明包 希望将其解释为有效的 SPDX 标识符,而无需 对于复杂且可能错误的启发式方法,并允许工具 轻松、明确地检测无效内容。License-Expression

这样可以避免两个误报 ( 包作者的值 没有明确打算作为显式 SPDX 标识符,但确实发生了 作为一个整体进行验证)和漏报(作者想要的表达方式) 是有效的 SPDX,但由于拼写错误或错误不是),否则是 无法清楚地区分真正的阳性和阴性,模棱两可 与这个 PEP 的目标不一致。License

此外,它允许现有字段和 许可证分类器更容易被弃用, 使用能够清晰地区分打算 是否肯定符合本 PEP 中的更新规范, 并相应地调整其行为(警告、错误等)。 否则,工具将不得不允许重复和潜在的 冲突的字段和分类器,或警告/错误 大量将 SPDX 标识符作为 字段的值,有意或无意(例如)。LicenseLicenseLicenseMIT

最后,它避免了更改现有元数据字段的行为, 并避免了工具必须猜测和现场行为 基于它的价值,而不仅仅是它的存在。Metadata-Version

虽然这意味着包含作为 SPDX 许可证表达式有效的字段的现有分发的子集不会自动 因此,这只需要在密钥上附加几个字符即可 name 在项目的源元数据中,此 PEP 提供广泛的 关于如何通过工具自动完成此操作的指南。License

鉴于这一切,决定继续定义一个新的, 专用字段 .License-Expression

重新使用带有值前缀的字段

作为前面的替代方法,在 SPDX 许可表达式前面加上以下前缀: 例如: 建议减少重用中固有的歧义 字段。然而,这实际上相当于创造 字段中的字段,并不能解决 保持领域。也就是说,它仍然会改变 现有元数据字段,需要工具来解析其值 确定如何处理其内容,并制定规范和 弃用过程更复杂,更不干净。spdx:LicenseLicense

然而,它仍然与创建一个新的具有相同的主要潜在缺点 字段:当前在字段中使用有效 SPDX 标识符的项目,无论有意还是无意,都不会被自动识别,并且需要 修复的工作量大致相同,即更改 项目的源元数据。因此,它被拒绝了,转而支持一个新领域。License

不要让相互排斥

对于向后兼容性,字段和/或许可证 分类器仍然可以与新字段一起使用,大概是有警告的。然而,这 很容易导致不一致,至少是重复的 许可不少于三个不同字段的元数据,即 这与这个 PEP 的目标完全相反,即制作许可故事 更简单,毫不含糊。因此,并与明确的社区协调一致 否则,这个想法被彻底拒绝了。LicenseLicense-Expression

不要弃用现有字段和分类器

一些社区成员最初担心弃用 现有的字段和分类器将导致 现有包作者的流失过多,并将门槛提高到 新人的条目,尤其是寻求 打包和发布他们的个人项目,而不必关心 太多关于法律技术细节或成为“执照律师”的信息。 事实上,每一次弃用都会带来一些非零的短期成本, 并且应相对于整体长期仔细考虑 净收益。至少,这种变化不应该让它变得更 对于普通的 Python 开发人员来说,很难分享他们的工作 他们选择的许可证,理想情况下可以改善这种情况。License

经过多轮提案、讨论和完善, 普遍的共识显然是赞成弃用这一遗产 指定许可证的方法,支持“一种明显的方法”, 改善当前围绕许可证的复杂和碎片化的故事 文档。如果不这样做,将留下三种不同的未弃用方式 为一个包指定一个许可证,其中两个不明确,小于 清晰/明显的使用方式,记录不一致且过时。 对于生态系统中要支持的所有工具来说,这更加复杂 无限期(而不仅仅是支持旧软件包的安装程序 实现以前的冻结元数据版本),导致一个不平凡的 以及无限制的维护成本。

此外,它为用户带来了更加复杂和混乱的局面 三个相似但截然不同的选项可供选择,尤其是年龄较大的选项 文档、答案和文章四处漂浮,建议不同的文档、答案和文章。 在这三者中,是最简单、最清晰易用的 正确;用户只需粘贴所需的许可证标识符,或选择它 通过工具,他们就完成了;无需了解 Trove 分类器和 仔细阅读列表以找出适用的选项(并感到困惑) 通过许多模棱两可的选择),或者自己弄清楚应该做什么 在密钥中(从无到有,到许可证文本, 到自由格式的描述,到相同的 SPDX 标识符 无论如何输入密钥,假设他们可以 轻松找到有关它的文档)。事实上,这可以是 多亏了新领域,变得更加容易。例如,Github 的热门 ChooseALicense.com 链接到如何添加 SPDX 许可证 支持的各种语言的项目源元数据的标识符 它们就在每个许可证页面的侧边栏中;SPDX 支持 PEP 允许将 Python 添加到该列表中。License-Expressionlicenselicense

对于已指定 或 license 的当前软件包维护者 分类器,此 PEP 仅建议警告并禁止错误 除发布工具外的所有工具,如果其预期,则允许出错 分销平台如此要求。一旦维护者准备好了 升级,适用于已使用 SPDX 许可证表达式的用户(意外或非意外) 这只需要在 项目的源元数据,以及那些具有许可证分类器的 映射到单个明确的许可证,或其他定义的案例(公共领域、 proprietary),他们只需要删除分类器并粘贴到 相应的许可证标识符。该 PEP 提供了广泛的指导和 示例,以及其他资源,以及 自动化工具无需人工更改即可解决此问题。 当前指定许可证元数据的更复杂情况可能 需要一些人为干预,但在大多数情况下,工具将能够 在此 PEP 中的映射之后提供选项列表,以及 这些通常是最有可能受到 现有许可证元数据的局限性,因此受益最大 通过此 PEP 中的新字段。License

最后,对于未维护的软件包,那些使用支持旧软件包的工具 元数据版本,或选择不提供许可证元数据的版本, 无论弃用与否,都不需要进行任何更改。

不要强制要求在 PyPI 上验证新字段

以前,虽然这个 PEP 确实包括包装的规范性指南 发布工具(如Twine),它没有提供具体的指导 对于 PyPI(或其他包索引),它们是否以及如何 应验证 OR 字段, 以及他们应该如何处理将它们与已弃用的字段或许可证分类器结合使用。这简化了规范 并且要么将 PyPI 上的实现推迟到以后的 PEP,要么给出 对 PyPI 的自由裁量权强制执行所述的不变量,以最小化 对包作者的中断。License-ExpressionLicense-FileLicense

但是,在字段与现有字段分开之前,这一直没有说明,这将使 验证更具挑战性和向后不兼容,破坏 现有软件包。随着这一变化,人们达成了明确的共识,即 应从一开始就验证新字段,保证所有 上传到 PyPI 的发行版,声明核心元数据版本 2.4 或更高,并且该字段将具有有效的 表达式,以便 PyPI 及其包和元数据的使用者 可以依靠遵循这里的规范。License-ExpressionLicenseLicense-Expression

同样可以扩展到新领域, 以确保它是有效的,并且法律要求的许可证文件是 存在,因此对 PyPI、用户和下游消费者来说是合法的 以分发包。(当然,这并不能保证 因为它最终依赖于作者来声明它们,但它有所改善 保证这一点,并允许将来这样做,如果社区这样做 决定。需要明确的是,这不需要任何上传的分发 拥有这样的元数据,只有当他们选择根据新的 规范在此 PEP 中,保证有效。License-File

源元数据键

与项目源元数据中的键相关的替代可能性。licensepyproject.toml

    

向表添加子项

添加了此 PEP 的先前工作草案和子项 到项目源元数据中的现有表,以并行 existing 和 subkeys。虽然这似乎可能是 乍一看,最明显的方法,它有几个严重的缺点 相对于这里最终采取的。expressionfileslicensefiletext

最突出的是,这意味着两种截然不同的元数据类型正在 在需要非常不同的处理的同一顶级密钥下指定, 此外,与之前的安排不同,子项不是相互的 exclusive,可以同时指定两者,并可能使用一些子项 是动态的,其他是静态的,并映射到不同的核心元数据字段。

此外,这会导致与将键标记为(假设旨在指定表键, 正如 PEP 似乎不准确地暗示的那样, 而不是核心元数据字段),因为其中一个或两个都会有 被视为 。 将许可证表达式和许可证文件分组到同一键下 强制采用“全有或全无”的方法,并在用户意图方面产生歧义。dynamic[project]dynamic

这还有进一步的缺点。用户和工具都需要 跟踪哪些字段与其他字段互斥, 大大增加了认知和代码的复杂性,进而增加了概率 的错误。从概念上讲,将许多不同的字段并列在 相同的密钥相当不和谐,并导致密钥和核心元数据字段之间的映射更加复杂,这与 PEP 621 不一致。 这会导致表命名和结构进一步偏离 来自各种流行包装的核心元数据和原生格式 使用它的工具。最后,这导致规格明显更多 与替代方案相比,理解和实施复杂而复杂。[project][project]

此 PEP 现在采用的方法,使用保留的顶级字符串值 的键,添加新键 并弃用表子项 ( 和 ), 避免了上面确定的大多数问题, 并导致整体设计更清晰、更干净。 它允许和被独立标记,分离两种独立类型的元数据 (在句法和语义上),将表键恢复到核心元数据字段的更接近 1:1 的映射, 并将两者的嵌套减少一个级别。 除了向文件添加一个额外的键外,没有显着的 后一种方法的明显缺点,因此它被用于此 PEP。licenselicense-fileslicensetextfilelicenselicense-filesdynamic[project]

添加子项而不是字符串值

仅向表中添加一个子项, 而不是使用保留的顶级字符串值, 对读者和作者来说会更明确, 符合这个 PEP 的目标。 但是,它仍然具有上面列出的缺点 不特定于包含密钥。expressionlicensefiles

相对于扁平字符串值, 它增加了冗长、复杂性和额外的嵌套级别, 并且需要用户和工具来记住和处理 子项的相互排斥性 并记住哪些是已弃用的,哪些不是, 而不是将表子项作为一个整体干净地弃用。 此外,它不太明确是现代使用的“默认”选择, 鉴于用户倾向于最简单和最明显的选项。 最后,遵循 PEP 621 中建议的指导似乎是合理的, 给定顶级字符串值是专门为此目的保留的。

定义新的顶级密钥

此 PEP 的早期版本在表下定义了一个新的顶层, 而不是使用密钥的保留字符串值。 对于读者和作者来说,这被认为更清晰、更明确, 符合本 PEP 的目标。license-expression[project]license

此外,虽然与现有工具格式(和核心元数据)的差异 字段名称)在 PEP 621 中有先例, 使用与大多数/所有当前工具同名的键 表示不同的东西(并映射到不同的核心元数据字段), 具有独特且不兼容的语法和语义,则不, 并可能给读者和作者带来困惑和歧义。

此外,根据项目源元数据规范, 这将允许单独标记密钥 对应于 和 元数据字段 如 避免回填田的潜在问题 从此 PEP 当前允许的字段 没有它作为动态 (这是不可能的,因为它们都映射到同一个顶级键)。[project]LicenseLicense-ExpressiondynamicLicenseLicense-Expressionlicense

然而,社区共识倾向于使用 现有键的顶级字符串值, PEP 621 为此目的保留:license

特意保留了许可证密钥的实际字符串值 out 以允许将来的 PEP 指定对 SPDX 表达式的支持 (同样的逻辑适用于任何类型的“类型”字段,指定什么 许可文件或文本表示)。

这更短,更简单,便于用户记忆和输入, 避免在利用现有密钥的同时添加新的顶级密钥, 引导用户使用许可证表达式作为默认表达式, 并遵循原始 PEP 621 中的设想。

此外,这允许完全弃用表值 在不弃用密钥本身的情况下, 并使它们本质上是相互排斥的,而用户不必记住 以及必须执行它的工具。

最后,与其他工具格式和底层核心元数据的一致性 不被视为足够的优先权 要覆盖使用现有密钥的优点, 而这些担忧大多得到了缓解 未在构建时指定旧版许可证到许可证表达式的转换, 显式指定回填字段时不, 以及这两个领域是相互排斥的事实, 因此,几乎没有实际需要区分哪个是动态的。dynamicLicensedynamic

因此,此 PEP 采用了顶级字符串值, 正如早先的工作草案临时规定的那样。license

添加要视为表达式的键

而不是使用保留的顶级字符串值 表中的键, 可以向表中添加一个子项 以控制是否(或字符串值) 被解释为自由文本或许可证表达式。这可能使 向后兼容性更加无缝,因为旧工具可以忽略 它总是被视为 ,而较新的工具会 如果设置得当,请知道将其视为许可证表达式。 事实上,PEP 621 似乎暗示了这种可能性 实现 SPDX 许可表达式的替代方法。license[project]typelicensetexttextlicensetype

但是,与上一项相同的所有缺点都适用于此处, 包括更复杂的项目之间的更复杂的映射 源元数据和核心元数据以及演示文稿之间的不一致 在工具配置、项目源元数据和核心元数据中, 一个不那么干净的弃用,进一步为它命名, 以及无法将一个而不是另一个标记为动态等。

此外,虽然理论上在短期内可能更容易一些 从长远来看,这意味着用户必须始终记住 指定正确的以确保其许可证表达式为 正确解释,这会增加工作量和出错的可能性;我们可以 从不安全更改默认值,同时确信用户 明白他们输入的是一个明确的许可证表达, 所有误报和漏报问题如上所述。type

因此,由于这些以及相同的原因,这种方法被拒绝了 对于支持不同字段的核心元数据, 我们在这里同样拒绝这一点,赞成 键的保留字符串值。License-Expressionlicense

必须标记为动态才能回填

中的密钥可能需要为 显式设置为动态,以便为核心元数据字段 自动回填自 键的顶级字符串值。 这将更明确地表示将完成填充, 严格来说,键没有(也不能)在 中指定,并且满足对字母的更严格的解释 本 PEP 修订的先前 PEP 621 规范。licensepyproject.tomlLicenselicenselicensepyproject.toml

但是,这似乎没有必要,因为它只是使用 键的静态逐字文本值,如指定 严格在这个 PEP 中。因此,任何符合要求的工具都可以轻而易举地, 仅使用静态数据确定且明确地推导出此数据 在文件本身中。licensepyproject.toml

此外,这实际上增加了显着的歧义,因为它意味着值 可能会被其他工具任意填充,这反过来又会妥协 并与新字段的值冲突,即 为什么这个 PEP 明确禁止这样做。因此,不将其标记为将确保它仅按照此 PEP 进行处理 要求。License-Expressiondynamic

最后,明确告知用户将其标记为 ,或者不标记为 控制填充行为似乎有点像故意的那样对字段的滥用,并阻止工具适应最佳 实践(填充、不填充等)随着时间的发展和演变而发展。dynamicdynamic

源元数据键

表中键考虑的替代方案,主要与 path/glob 类型处理。license-filespyproject.toml[project]

将子项添加到

而不是定义互斥和子键 的表键,我们可以 使用列表的子项和 用于如何解释它的子项。但是,后者不提供 与前者相比真正的优势,以换取需要更多的击键, 冗长和复杂,以及允许两者的灵活性较低, 或将来的另一个附加子键,以及需要 bikeshed 在子项名称上。因此,它被立即拒绝。pathsglobslicense-files[project]filestype

只接受逐字路径

可以完全禁止将 Glob 作为键的值,只允许逐字逐句的文字路径。 这将确保明确指定所有许可证文件,所有 找到并包含指定的许可证文件,以及源元数据 从最严格的意义上讲,是完全静态的,没有工具 必须检查项目源文件的其余部分才能准确确定 将包含哪些许可证文件以及哪些值 会的。这也将适度简化规范和工具的实现。license-filespyproject.tomlLicense-File

然而,实用性在这里再次击败了纯粹性。支持 Glob,并且 许多现有工具用于查找许可证文件,并显式使用 指定每个许可证文件的完整路径将不必要地繁琐 适用于具有供应商代码和依赖项的更复杂的项目。更多 至关重要的是,这将使意外错过所需的内容变得更加容易 legal 文件,以静默方式使包无法分发。

工具仍然可以静态且一致地确定要包含的文件, 仅基于用户显式指定的 glob 模式和 文件名,无需安装、执行其代码,甚至 检查其文件。此外,仍然明确允许工具发出警告 如果指定的 glob 模式(包括完整路径)与任何文件都不匹配。 当然,sdists、wheels 和其他 sdists 将拥有完整的静态列表 在其分发元数据中指定的文件。

也许最重要的是,这也将排除当前指定的 默认值,作为当前最流行的工具广泛使用,因此 对向后兼容性、工具一致性和安全性的重大突破 以及合理的默认功能,以避免无意的许可证违规。 当然,我们欢迎并鼓励作者指定他们的许可证 文件显式通过 table 子项,一旦他们意识到它并且 如果它适合他们的项目和工作流程。paths

只接受 glob 模式

相反,所有字符串都可以被视为 glob 模式。 这将略微简化规范和实现,避免额外的级别 的嵌套,并且更接近现有工具的配置格式。license-files

但是,只需几个字符,它就可以确保用户知道 无论它们是输入 glob 还是逐字路径。此外,允许 要指定为文本路径的许可证文件可避免边缘情况,例如 包含 glob 字符(或那些令人困惑甚至恶意相似的字符 对他们来说,如 PEP 672 中所述)。

包含显式值可确保生成的元数据在 最严格的术语含义,明确指定了所有许可证路径 在文件中,保证包含并带有早期 如果缺少任何错误。这是不切实际的,至少没有 如果我们必须假设这些项目,则对许多工作流存在严重限制 是 glob 模式,而不是文字路径。pathsLicense-Filepyproject.toml

这允许工具找到它们并知道核心元数据字段的确切值,而无需遍历 项目的源代码树和匹配 glob,可能更容易, 更高效、更可靠的程序化检查和处理。License-File

因此,鉴于成本相对较小,收益显著, 这一做法未被采纳。

推断是路径还是 glob

考虑了是否只允许指定字符串数组 直接用于键,而不是将其设置为带有 explicit 和 .这将稍微简单一些,并避免 额外的嵌套级别,并且更接近配置格式 现有工具。然而,它最终被拒绝,转而支持单独的, 互斥子项和表子项。license-filespathsglobspathsglobs

在实践中,它只在 ( vs ) 中保存了 6 个额外的字符,但允许 用户更明确地声明他们的意图,确保他们了解如何 这些值将被解释,并作为一个明确的指标 用于将它们解析为 glob 而不是逐字路径文本的工具。pyproject.tomllicense-files = [...]license-files.globs = [...]

这反过来又允许更合适、更明确指定的工具 每种情况的行为,其中许多是不可靠或不可能的 没有它,为了避免常见的陷阱,提供更多有用的反馈和 整体表现更明智、更直观。这些包括, 保证立即包含每个指定的文件 如果缺少一个,则引发错误,并使用 检查 glob 语法, 排除不需要的备份、临时文件或其他此类文件(作为当前工具 已经这样做),如果 glob 与任何文件不匹配,也可以选择性地发出警告。 这也避免了边缘情况(例如,包含 glob 字符的路径)和 依靠启发式方法来确定解释——这正是这个 PEP 的东西 力求避免。pathsglobs

还允许使用平面数组值

最初,在决定定义为 和 的表之后,考虑在键下制作一个顶级字符串数组,表示一个或另一个(可能,以匹配大多数 当前工具)。这稍微短一点,更简单,可以轻轻地允许 将用户推向首选的,并允许稍微干净的处理 空箱(目前,两者的处理方式相同)。license-filespathsglobslicense-filesglobs

但是,在最好的情况下,这又只能保存六个字符,并且在那里 不是一个显而易见的选择;是否从偏好的角度来看(两者都有 明确的用例和好处),也不知道哪个用户会自然而然地 假设。

扁平化可能比嵌套好,但面对歧义,用户 可能无法抗拒猜测的诱惑。要求用户显式指定 一个或另一个确保他们知道他们的输入将如何处理, 并且对其他人(包括人类和机器)来说更具可读性。它还使 规范和工具实现稍微复杂一些,它总是可以 将来添加,但不会在不向后中断的情况下删除 兼容性。最后,对于“首选”选项,这意味着有 不止一种明显的方法可以做到这一点。

因此,根据 PEP 20,Python 的禅宗,特此拒绝这种方法。

    

允许 和 子项

考虑允许在表下同时指定 and 子项,因为它可能允许 更灵活地处理特别复杂的项目,并在 每个模式而不是整体基础是否条目 应视为 或 。pathsglobslicense-fileslicense-filespathsglobs

但是,鉴于现有建议的方法已经匹配或超过 工具配置文件中提供的那些功能和功能,没有 对此有明确的需求,而且很少有可能受益的案例,它增加了一个很大的 以相对最小的收益获得的复杂程度,就 规范,在工具实现和本身中。pyproject.toml

还有更多的边缘情况需要处理,例如如何处理文件 两个列表都匹配,并且在多个地方与当前 有关工具应如何与其中一个或另一个一起运行的规范,例如何时 没有文件匹配,保证包含的所有文件和文件路径 显式、静态指定等。

像以前一样,如果有明确的需要,总是可以允许的 将来以向后兼容的方式(在可能的范围内 首先),而不允许它的情况并非如此。 因此,决定要求这两个子项互斥。

将子项重命名为

最初,考虑是否改为命名表的子项。然而,最终 选择,因为调用表子项会导致 表名 () 和子项名 (),即 ,使它看起来像是首选的/ 默认子项,而不是默认子项,并且缺乏与描述字符串条目格式而不是当前格式相同的并行性 指向。pathslicense-filesfilespathsfileslicense-filesfileslicense-files.files = ["LICENSE.txt"]globs

必须标记为动态才能使用默认值

从表面上看,这似乎是明智的,至少有一个特别严格的限制 对 PEP 621 对列表的描述的解释,以 请考虑要求将密钥显式标记为 以便使用默认的 glob 模式,或者 以便匹配并包含许可证文件。dynamiclicense-filesdynamic

但是,这只是声明一个静态的、严格指定的默认值 对于此特定密钥,需要由所有符合要求的工具完全使用 (只要它没有被标记,就完全否定了这个论点), 并且不亚于用户本身的任何其他 glob 模式集的静态性 可以指定。此外,生成的核心元数据值 仍然可以仅使用源中的文件列表来确定,而没有 安装或执行任何代码,甚至检查文件内容。dynamicLicense-File

此外,即使不是这样,实用性也会胜过纯粹性,因为 解释将严格向后与现有的 格式,并且与现有工具的行为不一致。 此外,这将造成非常严重且可能的风险,即大量 项目在不知不觉中不再包含法律强制性的许可证文件, 使它们的分发在技术上是非法的,因此是不理智的, 更不明智的默认值。

最后,除了添加一行额外的默认要求样板之外 对于文件,不将默认值定义为动态允许作者清楚地 并明确指出他们的构建/打包工具何时将 处理许可证文件本身的包含,而不是严格 符合本 PEP 的项目源元数据部分; 否则将违背清单的主要目的 作为标记和逃生舱口。dynamic

许可证文件路径

与源中许可证文件的路径和位置相关的替代方法 并构建了发行版。

展平子目录中的许可证文件

该 PEP 的先前草案对处理许可证文件的问题保持沉默 在子目录中。目前,轮子和(遵循其 示例) Setuptools 项目展平所有许可证文件 添加到目录中而不保留源子目录 等级制度。.dist-info

虽然这是最简单的方法,并且符合现有的临时做法, 这可能会导致名称冲突和许可证文件破坏其他文件, 对于如何解决它们,没有明显的定义行为,并将 软件包在法律上不可分发,但未向用户明确表明 其指定的许可证文件尚未包括在内。

此外,这会导致非 root 用户的相对文件路径不一致 在源、SDIST 和 wheel 之间许可文件,并阻止路径 在“静态”表元数据中给出的是真正的静态, 因为它们需要扁平化,并且可能会相互覆盖。 最后,源目录结构通常意味着有价值的信息 关于许可证的适用范围,以及在源中在哪里可以找到它们, 当将它们压平时,它会丢失,重建起来远非易事。[project]

为了解决这个问题,PEP 现在提议,两个 以上问题,再现了原文的源目录结构 许可证文件。这将完全解决 上面的问题,唯一的缺点是目录更加嵌套。与边缘情况自定义仍有冲突风险 文件名(例如 , ),但情况也是如此 使用以前的方法,实际上拼合的文件更少 进入根源,这实际上会降低风险。此外 以下建议将许可证文件根植于子目录下,完全消除了冲突和混乱问题。.dist-info.dist-infoRECORDMETADATAlicenses

以不同的方式解决名称冲突

而不是保留许可证文件的源目录结构 在目录中,我们可以指定一些其他机制 用于解决冲突,例如预置或追加父目录名称 到许可证文件名,向上遍历树,直到名称唯一, 以避免过度嵌套的目录。.dist-info

但是,这并不能解决路径一致性问题,需要 更多的讨论、协调和自行车棚,并进一步复杂化 规范和实现。因此,它被拒绝了 赞成更简单、更明显的解决方案,即仅保留 源子目录布局,正如许多利益相关者已经倡导的那样。

直接转储

以前,包含的许可证文件直接存储在内置车轮和已安装项目的顶级目录中。随后 现有的临时做法,确保了目前大多数现有的车轮都使用这种 功能将与新的功能相匹配,并使规范更简单,具有 许可证文件始终存储在与内核相同的位置 元数据,与分布类型无关。.dist-info

但是,这会导致目录更加混乱,乱七八糟 使用任意许可证文件和子目录,而不是分离 许可证进入他们自己的命名空间(根据 Python 的禅宗,PEP 20,是 “一个按喇叭的好主意”)。虽然目前规模较小,但仍有 与特定自定义许可证文件名冲突的风险 (例如,,)中,其中 仅当此处指定了其他文件时才会增加,并且 需要仔细限制用于避免的潜在文件名 可能与许可证相关文件的冲突。最后 将许可证放入其自己指定的子目录中将允许 快速、轻松、正确地列出、复制和操作的人员和工具 所有这些都是一次(例如在发行版包装、法律检查等中) 而不必从核心元数据中引用它们的每个路径。.dist-infoRECORDMETADATA.dist-info

因此,现在是确定替代方法的谨慎时机。 最简单和最明显的解决方案,正如轮子上的几个人所建议的那样 和 Setuptools 实现问题,就是简单地根目录许可证文件 相对于 的子目录。这很简单 实施并解决此处指出的所有问题,没有明确的显着性 相对于其他更复杂的选项的缺点。licenses.dist-info

它确实使规范变得更加复杂和不那么优雅,但是 实施应保持同样简单。这确实意味着轮子 在此更改之后生成的许可证将具有不同位置的许可证 比之前的那些,但由于这对于子目录中的那些已经是真的, 在此 PEP 之前,无法发现这些文件或 以编程方式访问它们,这似乎不太可能构成 实践中的重大问题。如果不是这样,这将更加困难 以后不可能改变,一旦现状标准化,工具就 依靠当前的行为,并且对 not 的吸收要大得多 仅简单地包含许可证文件,但也可能访问它们 使用核心元数据,如果我们要更改它,现在是时候了 (特别是因为我们已经引入了一个边缘情况更改,即如何 处理子目录中的许可证文件,以及其他改进)。

因此,后者已被纳入本 PEP 的当前草案。

向滚轮添加新类别

而不是在里面定义一个根许可证目录() 核心元数据目录 () 对于轮子,我们可以改为 定义一个新类别(大概还有相应的安装方案), 与目前包含在 Wheel Archive 中的其他内容类似, 专门用于许可证文件,称为(例如)。有人提到过这一点 由 wheel 创建者,并允许在更多地方安装许可证 适合平台且灵活,而不仅仅是目录 在网站路径中,并且可能在概念上比包含更清晰 他们在那里。licenses.dist-info.datalicenses.dist-info

但是,目前,这个 PEP 并没有实现这个想法,而且它是 推迟到未来的。这将大大增加复杂性和摩擦 对于这个 PEP,主要关注的是标准化现有实践 以及更新核心元数据规范。此外,这样做会 可能需要修改并指定安装方案 其中,与 Wheel、Installer 和其他工具一起,这将是一个 不平凡的事业。虽然可能稍微复杂一些 重新打包器(例如用于 Linux 发行版的重新打包器),当前提案仍然是 确保包含所有许可证文件,并位于单个专用目录中 (可以很容易地复制或重新定位到下游),因此应该仍然 大大改善了这方面的现状,而没有随之而来的复杂性。sysconfig

此外,这种方法并不完全向后兼容(因为它 对于简单地提取轮子的工具不透明),是一个更大的 偏离现有做法,将导致更多的不一致 从不同版本的车轮获得许可证安装位置。最后 这意味着许可证不会安装在靠近其位置的位置 关联代码时,许可证根路径中会有更多的可变性 跨平台以及已构建的发行版和已安装的项目之间, 以编程方式访问已安装的许可证会更加困难,并且 需要创建合适的安装位置和方法,讨论 并决定这样可以避免名称冲突。

因此,为了将此 PEP 保持在范围内,保留了当前的方法。

命名子目录

两者都被认为是潜在的 wheels 内部的根许可证目录的名称,以及 已安装的项目。PEP的初稿规定了前者 由于稍微清晰一些,并且与 核心元数据字段的名称 () 和表键 ()。 但是,PEP 的当前版本采用了以下名称: 由于社区普遍偏爱其较短的长度, 更简单,没有分隔符(、 等)。licenseslicense_files.dist-infoLicense-File[project]license-fileslicense_-

其他想法

杂项提案、可能性和讨论要点 最终没有被采用。

将标识符映射到许可证文件

这需要使用映射(因为两个并行列表太容易了 对齐错误),这将增加许可方式的额外复杂性 并添加额外的嵌套级别。

需要映射,因为不能保证所有表达式 (密钥)具有与之关联的单个许可证文件(例如 GPL 有例外,可能在单个文件中)和任何表达式 没有多个。(例如,Apache 许可证和 例如,它的文件是两个不同的文件)。 对于大多数常见情况,单个许可证表达式和一个或多个许可证 文件将是完全足够的。在更罕见和更复杂的情况下,其中 涉及许多许可证,作者仍然可以安全地使用这些字段 此处指定,只是由于没有指定哪个而略微失去了清晰度 文本文件映射到哪个许可证标识符(尽管这应该在 实践中,每个许可证标识符都具有相应的 SPDX 注册 完整的许可证文本),同时不强制使用更复杂的数据模型 (映射)在绝大多数不需要或不想要它的用户上。LICENSENOTICE

当然,我们可以有一个具有多种可能值类型的数据字段(它是一个 字符串,它是一个列表,它是一个映射!但这可能是一个混乱的根源。 例如,在 npm(历史上)和 Rubygems 中已经这样做了 (直到今天),因此工具需要测试元数据字段的类型 在代码中使用它之前,当用户对何时使用列表或 字符串。因此,这种方法被拒绝。

将标识符映射到源文件

如前所述,文件级通知超出了此 PEP 的范围, 而现有的公约可以 如果需要,已经使用,此处没有进一步的说明。SPDX-License-Identifier

不要冻结与特定 SPDX 版本的兼容性

此 PEP 可以省略指定特定的 SPDX 规范版本, 或一个用于有效许可证标识符列表,这将允许 随着规范的发展而更灵活地更新,没有其他规范 PEP 或同等学历。

但是,对未来的SPDX更新中断表示严重关切 与现有表达式和标识符兼容,保持当前 根据此 PEP 中的定义,元数据无效的包。需要 与此处的这些规范的特定版本的兼容性 以及 PEP 或类似的更新过程,可以避免这种意外情况, 并遵循其他包装生态系统的实践。

因此,决定指定一个最低版本 并且需要工具与之兼容,同时仍允许更新 只要它们不破坏向后兼容性。这样就可以 立即利用改进并接受新改进的工具 许可证,但也保持与版本的向后兼容 此处指定,平衡灵活性和兼容性。

源代码和二进制发行版的不同许可证

作为另一个用例,有人询问它是否在范围之内 PEP 用于处理二进制发行版的许可证表达式的情况 (wheel) 与源分布 (sdist) 不同,例如 就像编译和捆绑二进制文件的非纯 Python 包一样 在与项目本身不同的许可证下。引用的一个例子是 PyTorch,它包含来自 Nvidia 的 CUDA,它是免费的 可分发,但不是开源的。NumPy 和 SciPy 也有类似的问题,正如 此 PEP 的原始作者,现已解决这些情况。

然而,鉴于这里固有的复杂性和缺乏明显的 这样做的机制,事实上每个轮子都需要自己的许可证 信息,缺乏对 PyPI 的支持,无法在 基于每个发行版的存档,以及相对小众的用例,它是 确定超出此 PEP 的范围,并留给未来的 PEP 解决是否存在足够的需要和利益,以及适当的 可以找到机制。

    

未解决的问题

该字段应该回填还是相互排斥?

目前,该 PEP 明确允许,但并未正式建议或 需要,构建工具以 字段中的逐字文本。这将 大概可以提高向后兼容性,并被建议 由一些在话语线程上的人。另一方面,允许它确实如此 增加复杂性,减少清洁、一致的分离, 防止字段完全互斥 替换为新字段,并要求其值 火柴。LicenseLicense-ExpressionLicenseLicense-Expression

因此,有一个更具体和具体的 回填数据的基本原理和用例,并提供更全面的数据 考虑这种方法的任何潜在优点或缺点, 以便就此事达成最终共识,可以适当地 这里是有道理的。

因此,这里表达的现状是可以接受的,允许工具 有余地自己决定吗?如果这个 PEP 正式建议, 甚至要求,该工具回填此元数据(这大概会 一旦发布了元数据规范的重大修订版,就会被撤销)? 还是不应该明确允许、劝阻甚至禁止?

是否应允许自定义许可证标识符?

此 PEP 的当前版本保留了仅指定 使用 SPDX 定义的许可证标识符,以及显式定义的 自定义标识符,并处理项目有许可证但不是许可证的两种常见情况 具有可识别的 SPDX 许可证标识符。LicenseRef-Public-DomainLicenseRef-Proprietary

为了获得最大的灵活性,自定义许可证 可以允许标识符,这可能对利基有用 案例或公司环境不是 适当或不够具体,但依赖主流 Python 构建工具,元数据字段仍然 希望用于此目的。LicenseRef-<CUSTOM-TEXT>LicenseRef-ProprietaryLicense-Expression

然而,这样做的缺点是不能捕捉到 规范定义的许可证标识符,从而生成许可证元数据 这与作者的意图以及用户的预期不匹配 可能认为他们必须在有效之前预置 许可证标识符,因为之前似乎有一些混淆。 此外,这鼓励了定制许可证标识符的激增, 这消除了实现清晰、明确和良好的目的 了解为其创建此 PEP 的许可证元数据。LicenseRef

事实上,对于需要特定、专有的定制许可证的利基案例, 他们总是可以简单地指定 ,然后 包括明确标识许可证所需的实际许可证文件 无论(如果不使用 SPDX 许可证标识符)在字段下。要求符合标准的工具允许自定义许可证 标识符似乎不是很有用,因为标准工具无法识别 定制的或知道如何对待它们。相比之下,定制工具, 在任何情况下都需要了解自定义标识符并对其采取行动, 明确允许,有充分的理由(因此是关键字) 不要求许可证标识符符合此处列出的标识符。 因此,该规范仍然允许在私营企业中使用 环境或特定的生态系统,同时避免了 将它们强加于所有主流包装工具上。LicenseRef-ProprietaryLicense-FileSHOULD

作为替代方法,文本标识符可以是 定义,这将更明确地表明许可证不能 用定义的标识符表示,并且应引用许可证文本 细节,不带负面和可能不合适 的含义。这将避免主要 提到的缺点(拼写错误、混淆、许可证激增) 允许任意 的 批准方法,而 解决为它引用的几个潜在的理论场景。LicenseRef-CustomLicenseRef-ProprietaryLicenseRef

另一方面,正如 SPDX 旨在(并且通常确实)涵盖所有 FSF 认可的“自由”和 OSI 认可的“开源”许可证, 这些来源保持密切同步,现在相对稳定, 这些范围之外的任何内容通常都会被 所涵盖,因此不那么具体 在这方面,有点多余。此外,它可能会产生误导 具有复杂/多个许可证的项目的作者,他们应该使用它 过度指定许可证表达式。LicenseRef-ProprietaryLicenseRef-Custom

目前,PEP保留了对其中任何一个的现有方法,因为 用例和收益被判断为足够边际 关于当前对包装格局的理解。对于这两个 但是,如果出现更具体的用例,这当然可以 重新考虑,无论是当前的 PEP 还是未来的 PEP(之前或 与实际删除遗留的非结构化元数据字段同时)。现在不定义它允许以后允许它 (或者现在仍然使用自定义打包工具),而不会影响向后 兼容性,而如果现在和以后允许它们,情况就不是这样了 在实践中被确定为不必要或问题太大。License

附录:示例

基本示例

从版本 59.1.1 开始的 Setuptools 项目本身, 不在其自己的项目源元数据中使用该字段。 此外,它不再像以前那样明确指定 /,因为 Setuptools 依赖于它自己的自动 包含与常见模式匹配的许可证相关文件, 例如它使用的文件。Licenselicense_filelicense_filesLICENSE

它在其中包含以下与许可证相关的元数据:setup.cfg

[metadata]classifiers =    License :: OSI Approved :: MIT License

到此 PEP 的最简单迁移包括改用以下方法:

[metadata]license_expression = MIT

或者,在以下表中:[project]pyproject.toml

[project]license = "MIT"
然后,分发包的输出核心元数据将为:

License-Expression: MITLicense-File: LICENSE

该文件将存储在 sdist 和 wheel 中,并在安装时从那里解压缩到站点目录(例如)中; 是相应存档的根目录 以及核心元数据中 Setuptools 版本的版本。LICENSE/setuptools-${VERSION}/LICENSE/setuptools-${VERSION}.dist-info/licenses/LICENSEsite-packages/${VERSION}

高级示例

假设 Setuptools 将包含第三方项目的许可证 在 和 目录中出售的;具体说来:setuptools/_vendor/pkg_resources/_vendor

packaging==21.2
pyparsing==2.2.1
ordered-set==3.1.1
more_itertools==8.8.0

这些项目的许可证表达式包括:

packaging: Apache-2.0 OR BSD-2-Clause
pyparsing: MIT
ordered-set: MIT
more_itertools: MIT

涵盖两个 Setuptools 的综合许可证表达式 proper 及其供应商依赖项将包含这些元数据, 将所有许可证表达式合并为一个。这样的表达式可能是:

MIT AND (Apache-2.0 OR BSD-2-Clause)

此外,根据许可证的要求,相关的许可证文件 必须包含在包装中。假设文件包含文本 MIT 许可证和 Setuptools 使用的版权,以及 ;目录中的文件包含 Apache 2.0 和 2 条款 BSD 许可证文本,以及包装版权声明和许可证选择声明。LICENSEpyparsingmore_itertoolsordered-setLICENSE*setuptools/_vendor/packaging/

具体来说,我们假设许可证文件位于以下位置 项目源代码树中的路径(相对于项目根目录和):pyproject.toml

LICENSEsetuptools/_vendor/packaging/LICENSEsetuptools/_vendor/packaging/LICENSE.APACHEsetuptools/_vendor/packaging/LICENSE.BSD

综上所述,我们的将是:setup.cfg

[metadata]license_expression = MIT AND (Apache-2.0 OR BSD-2-Clause)license_files =    LICENSE    setuptools/_vendor/packaging/LICENSE    setuptools/_vendor/packaging/LICENSE.APACHE    setuptools/_vendor/packaging/LICENSE.BSD

在 的表中,包含许可证文件 通过子项显式指定,如下所示:[project]pyproject.tomlpaths

[project]license = "MIT AND (Apache-2.0 OR BSD-2-Clause)"license-files.paths = [    "LICENSE",    "setuptools/_vendor/LICENSE",    "setuptools/_vendor/LICENSE.APACHE",    "setuptools/_vendor/LICENSE.BSD",]

或者,通过 glob 模式匹配,这可能是:

[project]license = "MIT AND (Apache-2.0 OR BSD-2-Clause)"license-files.globs = [    "LICENSE*",    "setuptools/_vendor/LICENSE*",]

无论使用哪种方法,都会在发行版中输出核心元数据 将:

License-Expression: MIT AND (Apache-2.0 OR BSD-2-Clause)License-File: LICENSELicense-File: setuptools/_vendor/packaging/LICENSELicense-File: setuptools/_vendor/packaging/LICENSE.APACHELicense-File: setuptools/_vendor/packaging/LICENSE.BSD

在生成的 sdist 中,将存档的根目录和核心元数据中指定的 Setuptools 发行版版本, 许可证文件将位于以下路径:/${VERSION}

/setuptools-${VERSION}/LICENSE
/setuptools-${VERSION}/setuptools/_vendor/packaging/LICENSE
/setuptools-${VERSION}/setuptools/_vendor/packaging/LICENSE.APACHE
/setuptools-${VERSION}/setuptools/_vendor/packaging/LICENSE.BSD

在构建的轮子中,作为存档的根目录,与前一个一样,许可证文件将存储在:/{version}

/setuptools-${VERSION}.dist-info/licenses/LICENSE
/setuptools-${VERSION}.dist-info/licenses/setuptools/_vendor/packaging/LICENSE
/setuptools-${VERSION}.dist-info/licenses/setuptools/_vendor/packaging/LICENSE.APACHE
/setuptools-${VERSION}.dist-info/licenses/setuptools/_vendor/packaging/LICENSE.BSD

最后,在已安装的项目中,作为站点目录 与前一个一样,许可证文件将安装到:site-packages{version}

site-packages/setuptools-${VERSION}.dist-info/licenses/LICENSE
site-packages/setuptools-${VERSION}.dist-info/licenses/setuptools/_vendor/packaging/LICENSE
site-packages/setuptools-${VERSION}.dist-info/licenses/setuptools/_vendor/packaging/LICENSE.APACHE
site-packages/setuptools-${VERSION}.dist-info/licenses/setuptools/_vendor/packaging/LICENSE.BSD

表达式示例

有效值的一些其他示例:License-Expression

License-Expression: MITLicense-Expression: BSD-3-ClauseLicense-Expression: MIT AND (Apache-2.0 OR BSD-2-clause)License-Expression: MIT OR GPL-2.0-or-later OR (FSFUL AND BSD-2-Clause)License-Expression: GPL-3.0-only WITH Classpath-Exception-2.0 OR BSD-3-ClauseLicense-Expression: LicenseRef-Public-Domain OR CC0-1.0 OR UnlicenseLicense-Expression: LicenseRef-Proprietary

附录:用户场景

下面从用户的角度介绍了一系列常见用例, 为每个人提供直接的指导。请注意以下几点 不应被视为法律建议,读者应咨询 在其管辖范围内获得许可的法律从业者,如果他们不确定 他们情况的具体情况。

我有一个不会分发的私有包

如果您的软件包未公开共享,即在公司外部, 组织或家庭,通常不是严格必要的 一个正式的许可证,所以你不一定需要在这里做任何额外的事情。

但是,最好在软件包配置中包含许可证表达式,和/或 文件中的版权声明和任何法律声明 在项目目录的根目录中,这将自动 包含在打包工具中。LicenseRef-ProprietaryLICENSE.txt

我只想分享我自己的作品,不受法律限制

虽然您不需要包含许可证,但如果您不这样做,则没有人有权下载、使用或改进您的作品, 所以这可能与你实际想要的相反。 相反,麻省理工学院许可证是一个不错的选择,因为它很简单, 广泛使用,允许任何人对您的工作做任何他们想做的事情 (除了起诉你,你可能也不想要)。

要应用它,只需将文本粘贴到存储库根目录下命名的文件中,然后将年份和您的姓名添加到 版权行。然后,只需在您的打包工具支持的情况下添加它, 或在其配置文件/部分(例如,在 中的 Setuptools 下)。大功告成!LICENSE.txtlicense = "MIT"[project]pyproject.tomllicense_expression = MIT[metadata]setup.cfg

我想在特定许可证下分发我的项目

要使用特定许可证,只需将其文本粘贴到存储库根目录下的文件中(如果文件开头为 或已经包含),然后在打包工具支持它时将其添加到 under 中,或者在其 配置文件(例如,对于 Setuptools,在 中)。您可以在 ChooseALicense 或 SPDX 等网站上找到可复制的许可证文本。LICENSE.txtLICENSECOPYINGlicense = "LICENSE-ID"[project]pyproject.tomllicense_expression = LICENSE-ID[metadata]setup.cfgLICENSE-ID

许多流行的代码主机、项目模板和打包工具都可以添加 许可证文件,将来也可能支持该表达式。

我维护一个已获得许可的现有软件包

如果您的项目中已有许可证文件和元数据,则可以 应该只需要做一些调整就可以利用新的 功能性。

在项目配置文件中,在 ( table in ), (Setuptools / ), 下输入许可证表达式 或您的包装工具的等效产品, 并确保删除任何旧表子项或分类器。您的现有价值可能已经存在 作为一个有效(例如,,等); 否则,请检查 SPDX 许可证列表中的标识符 与项目中使用的许可证相匹配。license[project]pyproject.tomllicense_expressionsetup.cfgsetup.pylicenseLicense ::licenseMITApache-2.0 OR BSD-2-Clause

如果许可证文件以 、 或 开头,或者您已将打包工具配置为添加它们 (例如 在),你应该已经准备好了。 如果没有,请确保将它们列在 in 或 in 下(如果您的工具支持它),或者在工具的配置文件中列出它们 (例如 在 Setuptools 中)。LICENSECOPYINGNOTICEAUTHORSlicense_filessetup.cfglicense-files.pathslicense-files.globs[project]pyproject.tomllicense_filessetup.cfg

请参阅基本示例,了解简单但完整的真实世界演示 这在实践中是如何工作的,包括一些额外的技术细节。 打包工具可能支持自动转换旧许可 元数据;有关详细信息,请查看工具的文档。

我的软件包包含不同许可证下的其他代码

如果您的项目包含来自不同许可证所涵盖的其他许可证的代码, 例如供应商依赖项或从其他开源复制的文件 软件,您可以构造一个许可证表达式(或有一个工具 帮助您这样做)来描述所涉及的许可证和关系 在他们之间。

简而言之,意味着两个许可证都适用 添加到您的项目或项目的一部分(例如,您包含了一个文件 在另一个许可证下),并表示可以根据用户的选择使用其中任何一个许可证(例如, 您希望允许用户选择多个许可证)。你可以使用 括号 () 用于分组以形成涵盖最多内容的表达式 复杂的情况。License-1 AND License-2License-1 OR License-2()

在项目配置文件中,在 ( table of ), (Setuptools / ), 下输入许可证表达式 或您的包装工具的等效产品, 并确保删除任何旧表子项 或分类器。license[project]pyproject.tomllicense_expressionsetup.cfgsetup.pylicenseLicense ::

此外,请确保将所有许可证的完整许可证文本添加为文件 项目存储库中的某个位置。如果它们都在根目录中 并以 、 或 开头 , 它们将自动包含在内。否则,您需要列出 相对路径或 glob 模式,它们分别位于 in 或 in 下(如果工具支持),或者在工具的配置文件中 (例如 在 Setuptools 中)。LICENSECOPYINGNOTICEAUTHORSlicense-files.pathslicense-files.globs[project]pyproject.tomllicense_filessetup.cfg

例如,如果您的项目已获得麻省理工学院的许可,但已合并 根据 无论是 Apache 2.0 还是 2 条款 BSD,您的许可证表达式都会 是。你可能在存储库根目录中有 a,在子目录中有一个 and,因此要包括 所有这些,都可以指定为 glob 模式或文本文件路径。packagingMIT AND (Apache-2.0 OR BSD-2-Clause)LICENSE.txtLICENSE-APACHE.txtLICENSE-BSD.txt_vendor["LICENSE.txt", "_vendor/packaging/LICENSE*"]["LICENSE.txt", "_vendor/LICENSE-APACHE.txt", "_vendor/LICENSE-BSD.txt"]

查看完整的高级示例,了解全面的端到端 将其应用于现实世界的复杂项目,具有大量的技术 详细信息,并查阅教程以获取更多帮助和示例 使用 SPDX 标识符和表达式。

附录:Python 中的许可证文档

有多种方法可以使用或推荐用于记录 Python 项目 今天的许可证。下面列出了最常见的。

核心元数据

有两个重叠的核心元数据字段用于记录许可证: 许可证字符串以 和 字段为前缀作为自由文本。ClassifierLicense ::License

核心元数据字段文档目前是:License

License=======.. versionadded:: 1.0

Text indicating the license covering the distribution where the license
is not a selection from the "License" Trove classifiers. See:ref:`"Classifier" <metadata-classifier>` below.
This field may also be used to specify a
particular version of a license which is named via the ``Classifier``field, or to indicate a variation or exception to such a license.

Examples::    License: This software may only be obtained by sending the            author a postcard, and then the user promises not            to redistribute it.    License: GPL version 3, excluding DRM provisions

尽管有两个领域,但有时很难传达任何东西 但许可更简单。例如,一些分类器缺乏精度 (没有版本的 GPL)以及当多个许可证分类器 列出,目前尚不清楚是否必须同时申请两个许可证,或者用户可以选择 在他们之间。此外,可用的许可证分类器列表 相当有限和过时。

设置工具和滚轮

除了许可证代码或限定符之外,还记录了许可证文本文件,并且 隐式或显式包含在构建的包中, 这是另一个可能的混淆来源:

在 Setuptools 和 Wheel 项目中, 许可证文件会自动添加到分发中(在其源中 在源代码发行版/SDISTT 中的位置,以及在构建的 wheel 的目录中),如果它们与许多通用许可证之一匹配 文件名模式(、 和 )。或者,包作者可以指定许可证列表 要包含在 键 in 下的内置轮子中的文件路径 项目的部分 ,或作为参数 到函数。目前,跟随轮子 作为项目的负责人,Setuptools 将收集到的许可证文件扁平化为 元数据目录,破坏同名文件,并转储许可证 文件直接添加到顶级目录中,但希望解决这两个问题, 取决于此 PEP 被接受。.dist-infoLICEN[CS]E*COPYING*NOTICE*AUTHORS*license_files[metadata]setup.cfgsetuptools.setup().dist-info

这两个工具还支持一个较旧的单数参数,即 仅允许指定一个要添加到分发的许可证文件,即 已被弃用一段时间,但仍有一些用途。license_file

在此 PEP 的早期草案发布后,Setuptools 添加了对分发的支持 元数据,如本规范所述。这允许其他工具 使用生成的元数据来明确定位许可证文件 对于给定的包。License-File

PyPA 打包指南和示例项目

PyPA 初学者打包教程及其更多内容 综合包装指南指出它是 重要的是,每个软件包都包含一个许可证文件。他们以 官方 PyPA 示例项目为例,该项目明确列在 其,遵循本 PEP 正式规定的现有做法。LICENSE.txtlicense_filessetup.cfg

初学者打包教程和示例项目都只使用分类器来声明 包的许可证,并且不要包含或提及该字段。 完整的包装指南确实提到了这个领域,但是 声明作者应改用许可证分类器,除非 Project 使用非标准许可证(本指南不鼓励这样做)。License

Python 源代码文件

注意:在源代码中记录许可证不在本 PEP 的范围内。

除了使用注释和/或约定之外, 许可证有时记录在 Python 代码文件中,使用 一个“dunder”模块级常量,通常命名为 。SPDX-License-Identifier__license__

这一惯例虽然可能有些过时,但已得到 内置功能和标准模块。 dunder 变量将显示在模块的 DATA 部分中。help()pydochelp()

其他 Python 打包工具

Conda 包清单支持 和 字段,并自动包含许可证文件 遵循与 Wheel 和 Setuptools 项目类似的命名模式。licenselicense_file

Flit 建议使用分类器而不是字段(根据当前的 PyPA 打包指南)。License

PBR 使用与 Setuptools 类似的数据,但始终存储在 中。setup.cfg

Poetry 指定了密钥与 SPDX 许可证标识符的用法。licensepyproject.toml

附录:其他项目中的许可证文档

这是对其他地方如何做事的调查。

Linux 发行版软件包

  • 注意:在大多数情况下,包括最常见许可证的文本 全局共享文档目录(例如 )。/usr/share/doc

  • Debian 使用机器可读的版权文件来记录软件包许可证。 它定义了自己的许可证表达式语法和标识符列表 通用许可证,两者都与 SPDX 的许可证密切相关。

  • Fedora 软件包指定了如何包含许可证文本并使用必须填写的许可证字段 从广泛的列表中使用适当的短许可证标识符 的“良好许可证”。Fedora 也定义了自己的 许可证表达式语法,类似于 SPDX。

  • OpenSUSE 软件包将 SPDX 许可证表达式与 SPDX 许可证 ID 和其他许可证标识符列表。

  • Gentoo ebuild使用一个变量。 这个字段在GLEP-0023和Gentoo开发手册中都有指定。 Gentoo还定义了一个允许的许可证列表和一个许可证表达式 语法,这与 SPDX 有很大不同。LICENSE

  • FreeBSD 软件包 Makefile 提供了 和 字段 和 自定义许可证符号列表。为 非标准许可证, FreeBSD 建议使用 和 添加和字段,以及用于限定许可证权限和记录许可证分组的复杂功能。允许记录更多 多个许可证以及它们如何一起应用,形成自定义许可证 表达式语法。FreeBSD 还推荐在源代码文件中使用LICENSELICENSE_FILELICENSE=UNKNOWNLICENSE_NAMELICENSE_TEXTLICENSE_PERMSLICENSE_GROUPSLICENSE_COMBSPDX-License-Identifier

  • Arch Linux PKGBUILD 定义了自己的许可证标识符。 如果未定义许可证,则可以使用该值。'unknown'

  • OpenWRT ipk 软件包使用 and 变量,并建议使用 SPDX 许可证 标识符。PKG_LICENSEPKG_LICENSE_FILES

  • NixOS 使用 SPDX 标识符和一些额外的许可证 ID 在其许可证字段中。

  • GNU Guix(基于 NixOS)有一个 License 字段,使用自己的许可证符号列表,并指定如何使用一个许可证或它们的列表。

  • Alpine Linux 软件包建议在 license 字段。

语言和应用程序包

  • java 中,Maven POM 定义了一个带有列表的 XML 标记 的许可证,每个许可证都有名称、URL、注释和“分发”类型。 这不是强制性的,并且未指定每个字段的内容。licenses

  • JavaScript NPM package.json使用单个许可证字段 SPDX 许可证表达式或 ID(如果未指定)。 许可证文件可以作为在单个字段中使用的替代文件引用。UNLICENSEDSEE LICENSE IN <filename>license

  • Rubygems gemspec 指定单个许可证或许可证列表 字符串。多个许可证之间的关系 列表未指定。他们建议使用 SPDX 许可证标识符。

  • CPAN Perl 模块使用单个许可证字段,该字段是 单个字符串或字符串列表。许可证之间的关系 未指定列表。有一个自定义许可证标识符列表以及 这些通用标识符:、、、。open_sourcerestrictedunrestrictedunknown

  • Rust Cargo 指定使用 SPDX 许可证表达式 (v2.1) 在现场。它还支持替代表达式 语法使用斜杠分隔的 SPDX 许可证标识符,并且还有一个字段。crates.io 包注册表要求在以下情况下设置 or 字段 上传包。licenselicense_filelicenselicense_file

  • PHP composer.json 使用一个字段 SPDX 许可证 ID 或 .该字段是 单个字符串,类似于 SPDX 许可证表达式语法 with 和 keywords;或者是字符串列表,如果有 (分离)许可证的选择。licenseproprietarylicenseandor

  • NuGet 包以前仅使用简单的许可证 URL,但 现在指定使用 SPDX 许可证表达式和/或许可证的路径 包中的文件。NuGet.org 存储库声明它们仅 接受“由开源促进会批准”的许可证表达式 或自由软件基金会。

  • Go 语言模块没有提供任何元数据 依赖。许可信息留给代码作者和其他 要记录的社区包管理器。go.mod

  • Dart/Flutter 规范建议使用一个文件,该文件应包含所有许可证文本,每个文本由一行分隔 有 80 个连字符。LICENSE

  • JavaScript Bower 字段是单个字符串 或使用 SPDX 许可证标识符或路径/URL 的字符串列表 到许可证文件。license

  • Cocoapods podspec 字段要么是单个 字符串,或带有 和 键的映射。 除非提供 / 文件,否则这是强制性的。licensetypefiletextLICENSELICENCE

  • Haskell Cabal 接受 SPDX 许可证表达式,因为 版本 2.2。使用的 SPDX 许可证列表的版本是 阴谋集团版本。该规范还提供了 旧版(SPDX 之前)和 SPDX 许可证标识符。阴谋集团还指定了一个字段,列出要安装的许可证文件 包。license-file(s)

  • Erlang/Elixir mix/hex 包将字段指定为 必需的许可证字符串列表,并建议使用 SPDX 许可证 标识符。licenses

  • D 语言配音包定义了自己的许可证列表 标识符和许可证表达式语法,类似于 SPDX 标准。

  • R 包 DESCRIPTION 定义了自己的复杂许可证 表达式语法和许可证标识符列表。R 有一种独特的方式 支持许可证版本的说明符(例如 ) 在其许可证表达式语法中。LGPL (>= 2.0, < 3)

其他生态系统

  • 标题很简单 约定,用于在文件中记录许可证。SPDX-License-Identifier

  • 自由软件基金会 (FSF) 提倡使用 SPDX 许可证标识符,用于 GPL 和其他 版本化的自由软件许可证。

  • 欧洲自由软件基金会 (FSFE) 的 REUSE 项目提倡使用 .SPDX-License-Identifier

  • Linux 内核使用 FSFE 重用约定的一部分来记录其许可证。SPDX-License-Identifier

  • U-Boot率先在代码中使用 现在遵循 Linux 方法。SPDX-License-Identifier

  • Apache Software Foundation 项目将 RDF DOAP 与 指向 SPDX 许可证标识符的单个许可证字段。

  • Eclipse 基金会提倡使用 .SPDX-license-Identifiers

  • ClearlyDefined 项目促进使用 SPDX 许可证标识符和表达式,以提高许可证的清晰度。

  • Android 开源项目使用空标记文件,其中是许可证代码,例如 、 、 等。它还使用包含许可证和 通知文本。MODULE_LICENSE_XXXXXXBSDAPACHEGPLNOTICE

确认

  • 艾莉莎·科格兰(Alyssa Coghlan)

  • 凯文·弗莱明

  • 普拉丁·格丹

  • 奥列格·格伦鲁斯

  • 达斯汀·英格拉姆

  • 克里斯·杰多内克

  • 西里尔·罗兰特

  • Luis Villa(路易斯别墅)

版权

本文档位于公共领域或CC0-1.0-Universal许可协议下,以更宽松者为准。

The End 微信扫一扫

文章声明:以上内容(如有图片或视频在内)除非注明,否则均为腾龙猫勺儿原创文章,转载或复制请以超链接形式并注明出处。

本文作者:猫勺本文链接:https://www.jo6.cn/post/53.html

上一篇 下一篇

相关阅读

发表评论

访客 访客
快捷回复: 表情:
评论列表 (暂无评论,150人围观)

还没有评论,来说两句吧...

取消
微信二维码
微信二维码
支付宝二维码