PEP 440 – 版本识别和依赖规范
- 作者:
- Alyssa Coghlan <ncoghlan at gmail.com>, Donald Stufft <donald at stufft.io>
- BDFL 委托:
- Alyssa Coghlan <ncoghlan at gmail.com>
- 讨论至:
- Distutils-SIG 邮件列表
- 状态:
- 最终版
- 类型:
- 标准跟踪
- 主题:
- 打包
- 创建日期:
- 2013年3月18日
- 发布历史:
- 2013年3月30日, 2013年5月27日, 2013年6月20日, 2013年12月21日, 2014年1月28日, 2014年8月8日, 2014年8月22日
- 取代:
- 386
- 决议:
- Distutils-SIG 消息
摘要
本 PEP 描述了一种用于识别 Python 软件发行版版本并声明特定版本依赖关系的方案。
定义
本文件中的关键词“必须”、“不得”、“需要”、“应”、“不应”、“建议”、“不建议”、“可以”和“可选”应根据RFC 2119的描述进行解释。
“项目”是可用于集成到其他软件组件中的软件组件。项目包括 Python 库、框架、脚本、插件、应用程序、数据或其他资源的集合,以及它们的各种组合。公共 Python 项目通常在Python 包索引上注册。
“发布版本”是项目的唯一标识快照。
“发行版”是用于发布和分发发布版本的打包文件。
“构建工具”是旨在在开发系统上运行,生成源和二进制发行版档案的自动化工具。构建工具也可以由集成工具调用,以便构建以 sdists 而非预构建二进制档案形式分发的软件。
“索引服务器”是活跃的发行版注册表,发布版本和依赖元数据并对允许的元数据施加限制。
“发布工具”是旨在在开发系统上运行并将源和二进制发行版档案上传到索引服务器的自动化工具。
“安装工具”是专门用于在部署目标上运行的集成工具,它们从索引服务器或其他指定位置获取源和二进制发行版档案,并将其部署到目标系统。
“自动化工具”是一个统称,涵盖构建工具、索引服务器、发布工具、集成工具以及任何其他生成或使用发行版版本和依赖元数据的软件。
版本方案
发行版由支持所有定义的版本比较操作的公共版本标识符标识。
版本方案用于描述特定发行版档案提供的发行版版本,以及对构建或运行软件所需的依赖版本施加约束。
公共版本标识符
规范的公共版本标识符必须符合以下方案:
[N!]N(.N)*[{a|b|rc}N][.postN][.devN]
公共版本标识符不得包含前导或尾随空格。
公共版本标识符在给定发行版中必须是唯一的。
安装工具应忽略不符合此方案的任何公共版本,但必须包含下面指定的规范化。安装工具在检测到不合规或模糊的版本时可以警告用户。
另请参阅附录 B : 使用正则表达式解析版本字符串,其中提供了检查与规范格式严格一致性的正则表达式,以及更宽松的正则表达式,接受可能需要后续规范化的输入。
公共版本标识符分为最多五个部分:
- 纪元段:
N! - 发布段:
N(.N)* - 预发布段:
{a|b|rc}N - 后期发布段:
.postN - 开发发布段:
.devN
任何给定的发布版本都将是“最终发布版本”、“预发布版本”、“后期发布版本”或“开发发布版本”,如下面各节所定义。
所有数字组件必须是非负整数,表示为 ASCII 数字序列。
所有数字组件必须根据其数值进行解释和排序,而不是作为文本字符串。
所有数字组件可以为零。除了下面针对发布段描述的情况外,零的数字组件没有特殊意义,只是在版本排序中始终是可能的最低值。
注意
为了更好地适应现有公共和私人 Python 项目中广泛的版本控制实践,此方案允许一些难以阅读的版本标识符。
因此,PEP 在技术上允许的一些版本控制实践强烈不鼓励用于新项目。在这种情况下,相关细节将在以下各节中注明。
本地版本标识符
本地版本标识符必须符合以下方案:
<public version identifier>[+<local version label>]
它们由一个正常的公共版本标识符(如前一节所定义)以及一个任意的“本地版本标签”组成,两者之间用加号分隔。本地版本标签没有分配特定的语义,但施加了一些语法限制。
本地版本标识符用于表示与上游项目完全 API(如果适用,还有 ABI)兼容的修补版本。例如,这些可能由应用程序开发人员和系统集成商通过应用特定的回溯错误修复来创建,因为升级到新的上游版本会对应用程序或其他集成系统(例如 Linux 发行版)造成太大干扰。
包含本地版本标签使得区分上游发布版本和下游集成商可能更改的重建版本成为可能。使用本地版本标识符不影响发布版本的类型,但如果应用于源发行版,则表示它可能不包含与相应上游发布版本完全相同的代码。
为确保本地版本标识符可以轻松地作为文件名和 URL 的一部分,并避免十六进制哈希表示中的格式不一致,本地版本标签必须限制在以下允许的字符集内:
- ASCII 字母 (
[a-zA-Z]) - ASCII 数字 (
[0-9]) - 句点 (
.)
本地版本标签必须以 ASCII 字母或数字开头和结尾。
本地版本的比较和排序分别考虑本地版本的每个段(由.分隔)。如果一个段完全由 ASCII 数字组成,则该段应被视为整数进行比较;如果一个段包含任何 ASCII 字母,则该段进行不区分大小写的字典比较。当比较数字段和字母段时,数字段始终比较为大于字母段。此外,具有更多段的本地版本将始终比较为大于具有更少段的本地版本,只要较短的本地版本的段与较长的本地版本的段的开头完全匹配。
“上游项目”是定义自己公共版本的项目。“下游项目”是跟踪和重新分发上游项目的项目,可能会从上游项目的后期版本回溯安全和错误修复。
发布上游项目到公共索引服务器时,不应使用本地版本标识符,但可以用于标识直接从项目源创建的私人构建。下游项目在发布与公共版本标识符标识的上游项目版本 API 兼容但包含额外更改(如错误修复)的版本时,应使用本地版本标识符。由于 Python 包索引仅用于索引和托管上游项目,因此它不得允许使用本地版本标识符。
使用本地版本标识符的源发行版应提供 python.integrator 扩展元数据(如PEP 459中所定义)。
最终版本
仅由发布段和可选的纪元标识符组成的版本标识符称为“最终发布版本”。
发布段由一个或多个非负整数值组成,用点分隔:
N(.N)*
项目中的最终发布版本必须以一致递增的方式编号,否则自动化工具将无法正确升级它们。
发布段的比较和排序依次考虑发布段的每个组件的数值。当比较具有不同组件数量的发布段时,较短的段会根据需要用额外的零填充。
虽然此方案允许在第一个组件之后有任意数量的附加组件,但最常见的变体是使用两个组件(“major.minor”)或三个组件(“major.minor.micro”)。
例如
0.9
0.9.1
0.9.2
...
0.9.10
0.9.11
1.0
1.0.1
1.1
2.0
2.0.1
...
发布系列是指以共同前缀开头的一组最终发布编号。例如,3.3.1、3.3.5 和 3.3.9.45 都属于 3.3 发布系列。
注意
X.Y 和 X.Y.0 不被视为不同的发布编号,因为发布段比较规则在将其与包含三个组件的任何发布段进行比较时,隐式地将两组件形式扩展为 X.Y.0。
也允许基于日期的发布段。一个使用发布年份和月份的基于日期的发布方案示例:
2012.4
2012.7
2012.10
2013.1
2013.6
...
预发布版本
一些项目使用“alpha、beta、发布候选版本”的预发布周期,以支持在最终发布之前由用户进行测试。
如果作为项目开发周期的一部分使用,这些预发布版本通过在版本标识符中包含预发布段来指示:
X.YaN # Alpha release
X.YbN # Beta release
X.YrcN # Release Candidate
X.Y # Final release
仅由发布段和预发布段组成的版本标识符称为“预发布版本”。
预发布段由预发布阶段的字母标识符以及一个非负整数值组成。给定发布版本的预发布版本首先按阶段(alpha、beta、发布候选版本)排序,然后按该阶段内的数字组件排序。
安装工具可以接受公共发布段的c和rc发布版本,以处理一些现有的遗留发行版。
安装工具应将c版本解释为等同于rc版本(即c1表示与rc1相同的版本)。
构建工具、发布工具和索引服务器应禁止为公共发布段同时创建rc和c发布版本。
后期发布版本
有些项目使用后期发布来解决最终发布版本中不影响已分发软件的次要错误(例如,更正发布说明中的错误)。
如果作为项目开发周期的一部分使用,这些后期发布版本通过在版本标识符中包含后期发布段来指示:
X.Y.postN # Post-release
包含后期发布段但没有开发发布段的版本标识符称为“后期发布版本”。
后期发布段由字符串.post组成,后跟一个非负整数值。后期发布版本按其数值组件排序,紧跟在相应的发布版本之后,并在任何后续发布版本之前。
注意
强烈不鼓励使用后期发布版本来发布包含实际错误修复的维护发布版本。通常,最好使用更长的发布编号并为每个维护发布版本递增最后一个组件。
预发布版本也允许后期发布版本:
X.YaN.postM # Post-release of an alpha release
X.YbN.postM # Post-release of a beta release
X.YrcN.postM # Post-release of a release candidate
注意
强烈不鼓励创建预发布版本的后期发布版本,因为它会使版本标识符难以供人类读者解析。通常,只需通过递增数字组件来创建新的预发布版本会更清晰。
开发版本
一些项目会定期发布开发版本,系统打包者(特别是 Linux 发行版)可能希望直接从源代码控制创建早期发布版本,而这些版本不会与后来的项目发布版本冲突。
如果作为项目开发周期的一部分使用,这些开发发布版本通过在版本标识符中包含开发发布段来指示:
X.Y.devN # Developmental release
包含开发发布段的版本标识符称为“开发发布版本”。
开发发布段由字符串.dev组成,后跟一个非负整数值。开发发布版本按其数值组件排序,紧接在相应的发布版本之前(以及具有相同发布段的任何预发布版本之前),并在任何先前发布版本(包括任何后期发布版本)之后。
预发布版本和后期发布版本也允许开发发布版本:
X.YaN.devM # Developmental release of an alpha release
X.YbN.devM # Developmental release of a beta release
X.YrcN.devM # Developmental release of a release candidate
X.Y.postN.devM # Developmental release of a post-release
注意
尽管它们可能对持续集成很有用,但强烈不鼓励将预发布版本的开发发布版本发布到通用公共索引服务器,因为它会使版本标识符难以供人类读者解析。如果需要发布此类版本,则创建新的预发布版本并通过递增数字组件会更清晰。
强烈不鼓励后期发布版本的开发发布版本,但对于使用后期发布表示法进行包含代码更改的完整维护发布版本的项目来说,这可能是合适的。
版本纪元
如果包含在版本标识符中,纪元出现在所有其他组件之前,与发布段之间用感叹号分隔:
E!X.Y # Version identifier with epoch
如果未给出显式纪元,则隐式纪元为0。
大多数版本标识符将不包含纪元,因为只有当项目以一种使正常版本排序规则给出错误答案的方式*更改*其版本编号处理方式时,才需要显式纪元。例如,如果一个项目使用像2014.04这样的基于日期的版本,并希望切换到像1.0这样的语义版本,那么在使用正常排序方案时,新版本将被识别为比基于日期的版本*更旧*:
1.0
1.1
2.0
2013.10
2014.04
但是,通过指定明确的纪元,可以适当地更改排序顺序,因为来自较晚纪元的所有版本都排在来自较早纪元的版本之后:
2013.10
2014.04
1!1.0
1!1.1
1!2.0
规范化
为了保持与现有版本的更好兼容性,解析版本时必须考虑一些“替代”语法。这些语法在解析版本时必须予以考虑,但它们应“规范化”为上面定义的标准语法。
大小写敏感性
所有ASCII字母在版本中应被解释为不区分大小写,规范形式是小写。这允许诸如1.1RC1之类的版本,它们将被规范化为1.1rc1。
整数规范化
所有整数都通过内置的int()解释并规范化为输出的字符串形式。这意味着整数版本00将规范化为0,而09000将规范化为9000。这不适用于本地版本中字母数字段内的整数,例如1.0+foo0100,它已经是其规范形式。
预发布分隔符
预发布版本应允许在发布段和预发布段之间使用.、-或_分隔符。其规范形式是没有分隔符。这允许诸如1.1.a1或1.1-a1之类的版本,它们将被规范化为1.1a1。它还应允许在预发布指示符和数字之间使用分隔符。这允许诸如1.0a.1之类的版本,它们将被规范化为1.0a1。
预发布拼写
预发布版本允许额外的拼写,即alpha、beta、c、pre和preview,分别对应a、b、rc、rc和rc。这允许诸如1.1alpha1、1.1beta2或1.1c3之类的版本,它们分别规范化为1.1a1、1.1b2和1.1rc3。在每种情况下,额外的拼写应被视为与其规范形式等效。
隐式预发布编号
预发布版本允许省略数字,在这种情况下,它隐式假定为0。其规范形式是明确包含0。这允许诸如1.2a之类的版本,它被规范化为1.2a0。
后期发布分隔符
后期发布版本允许.、-或_分隔符,也允许完全省略分隔符。其规范形式是使用.分隔符。这允许诸如1.2-post2或1.2post2之类的版本,它们规范化为1.2.post2。与预发布分隔符一样,这也允许在后期发布指示符和数字之间使用可选分隔符。这允许诸如1.2.post-2之类的版本,它将被规范化为1.2.post2。
后期发布拼写
后期发布版本允许额外的拼写,即rev和r。这允许诸如1.0-r4之类的版本,它规范化为1.0.post4。与预发布版本一样,额外的拼写应被视为与其规范形式等效。
隐式后期发布编号
后期发布版本允许省略数字,在这种情况下,它隐式假定为0。其规范形式是明确包含0。这允许诸如1.2.post之类的版本,它被规范化为1.2.post0。
隐式后期发布
后期发布版本允许完全省略post指示符。当使用这种形式时,分隔符必须是-,不允许其他形式。这允许诸如1.0-1之类的版本被规范化为1.0.post1。这种特定的规范化不得与隐式后期发布编号规则结合使用。换句话说,1.0-不是有效版本,它也不规范化为1.0.post0。
开发版本分隔符
开发发布版本允许.、-或_分隔符,也允许完全省略分隔符。其规范形式是使用.分隔符。这允许诸如1.2-dev2或1.2dev2之类的版本,它们规范化为1.2.dev2。
隐式开发版本编号
开发发布版本允许省略数字,在这种情况下,它隐式假定为0。其规范形式是明确包含0。这允许诸如1.2.dev之类的版本,它被规范化为1.2.dev0。
本地版本段
对于本地版本,除了使用.作为段分隔符外,使用-和_也是可以接受的。其规范形式是使用.字符。这允许诸如1.0+ubuntu-1之类的版本被规范化为1.0+ubuntu.1。
前导 'v' 字符
为了支持常见的版本表示法v1.0,版本前面可以有一个单独的字面v字符。此字符必须出于所有目的而被忽略,并且应从版本的所有规范化形式中省略。带v和不带v的相同版本被认为是等效的。
前导和尾随空格
前导和尾随空格必须被静默忽略并从版本的所有规范化形式中删除。这包括" ", \t, \n, \r, \f和\v。这允许合理地处理意外的空格,例如1.0\n这样的版本,它规范化为1.0。
符合版本方案的示例
标准版本方案旨在涵盖公共和私人 Python 项目中广泛的识别实践。实际上,如果一个项目试图充分利用该方案提供的所有灵活性,将导致人类用户难以确定版本的相对顺序,尽管上述规则确保所有合规工具都会一致地排序它们。
以下示例说明了项目可能选择识别其发布版本的不同方法的一小部分,同时仍确保人类用户和自动化工具都能轻松确定“最新发布版本”和“最新稳定发布版本”。
简单的“主版本.次版本”版本控制
0.1
0.2
0.3
1.0
1.1
...
简单的“主版本.次版本.微版本”版本控制
1.1.0
1.1.1
1.1.2
1.2.0
...
带有 alpha、beta 和候选预发布版本的“主版本.次版本”版本控制
0.9
1.0a1
1.0a2
1.0b1
1.0rc1
1.0
1.1a1
...
带有开发版本、发布候选版本和用于次要更正的后期发布版本的“主版本.次版本”版本控制
0.9
1.0.dev1
1.0.dev2
1.0.dev3
1.0.dev4
1.0c1
1.0c2
1.0
1.0.post1
1.1.dev1
...
基于日期的发布版本,每年递增序列,跳过零
2012.1
2012.2
2012.3
...
2012.15
2013.1
2013.2
...
允许的后缀和相对排序总结
注意
本节主要面向自动处理分发元数据的工具的作者,而不是决定版本控制方案的 Python 分发开发者。
版本标识符的纪元段必须根据给定纪元的数值进行排序。如果不存在纪元段,则隐式数值为0。
版本标识符的发布段必须按照 Python 的元组排序方式进行排序,当规范化的发布段解析如下时:
tuple(map(int, release_segment.split(".")))
参与比较的所有发布段必须通过根据需要用零填充较短的段来转换为一致的长度。
在数字发布版本(1.0, 2.7.3)中,允许以下后缀,并且必须按所示顺序排序:
.devN, aN, bN, rcN, <no suffix>, .postN
请注意,c被认为在语义上等同于rc,并且必须像rc一样进行排序。工具可以拒绝在同一发布段中同时出现c和rc的相同N值的情况,认为其模糊不清,并保持符合 PEP。
在 alpha (1.0a1)、beta (1.0b1) 或发布候选版本 (1.0rc1, 1.0c1) 中,允许以下后缀,并且必须按所示顺序排序:
.devN, <no suffix>, .postN
在后期发布版本(1.0.post1)中,允许以下后缀,并且必须按所示顺序排序:
.devN, <no suffix>
请注意,devN和postN必须始终以点号开头,即使在紧跟数字版本之后使用也是如此(例如1.0.dev456, 1.0.post1)。
在一个具有共同前缀的预发布、后期发布或开发发布段中,排序必须按照数字组件的值进行。
以下示例涵盖了许多可能的组合:
1.dev0
1.0.dev456
1.0a1
1.0a2.dev456
1.0a12.dev456
1.0a12
1.0b1.dev456
1.0b2
1.0b2.post345.dev456
1.0b2.post345
1.0rc1.dev456
1.0rc1
1.0
1.0+abc.5
1.0+abc.7
1.0+5
1.0.post456.dev34
1.0.post456
1.0.15
1.1.dev1
不同元数据版本间的版本排序
元数据 v1.0 (PEP 241) 和元数据 v1.1 (PEP 314) 未指定标准版本识别或排序方案。然而,元数据 v1.2 (PEP 345) 确实指定了一个方案,该方案在PEP 386中定义。
由于简单安装程序 API 的性质,安装程序无法知道特定发行版使用的是哪个元数据版本。此外,安装程序需要能够创建一个合理优先的列表,该列表包含项目的所有或尽可能多的版本,以确定它应该安装哪个版本。这些要求使得需要对所有项目版本使用一个解析机制进行标准化。
鉴于上述情况,本 PEP 必须用于所有元数据版本,并取代PEP 386,即使是元数据 v1.2。工具应忽略任何无法通过本 PEP 中的规则解析的版本,但如果本 PEP 没有可用的合规版本,则可以回退到实现定义的版本解析和排序方案。
分发用户可能希望从他们控制的任何私有软件包索引中明确删除不合规的版本。
与其他版本方案的兼容性
有些项目可能选择使用需要转换才能符合本 PEP 定义的公共版本方案的版本方案。在这种情况下,项目特定版本可以存储在元数据中,而转换后的公共版本则发布在版本字段中。
这使得自动化分发工具能够提供一致正确的已发布版本排序,同时仍然允许开发人员为其项目使用他们偏爱的内部版本控制方案。
语义版本控制
语义版本控制是一种流行的版本识别方案,它比本 PEP 对发布版本号不同元素的含义规定性更强。即使一个项目选择不遵守语义版本控制的细节,该方案也值得理解,因为它涵盖了依赖其他发行版以及发布供他人依赖的发行版时可能出现的许多问题。
语义版本控制的“主版本.次版本.补丁版本”(在本 PEP 中描述为“主版本.次版本.微版本”)方面(2.0.0 规范中的条款 1-8)与本 PEP 中定义的版本方案完全兼容,并鼓励遵守这些方面。
包含连字符(预发布版本 - 条款 10)或加号(构建版本 - 条款 11)的语义版本与本 PEP 不兼容,不允许在公共版本字段中使用。
将此类基于语义版本控制的源标签转换为兼容公共版本的一种可能机制是使用.devN后缀来指定适当的版本顺序。
具体的构建信息也可以包含在本地版本标签中。
基于 DVCS 的版本标签
许多构建工具与 Git 和 Mercurial 等分布式版本控制系统集成,以便在版本标识符中添加一个标识哈希。由于哈希无法可靠排序,因此此类版本不允许在公共版本字段中使用。
与语义版本控制一样,公共.devN后缀可用于唯一标识此类发布版本进行发布,而原始基于 DVCS 的标签可存储在项目元数据中。
标识哈希信息也可以包含在本地版本标签中。
Olson 数据库版本控制
pytz项目继承了相应的 Olson 时区数据库版本控制方案:年份后跟一个表示当年数据库版本的英文字符。
这可以转换为符合要求的公共版本标识符,如<year>.<serial>,其中序列从零或一(对于“<year>a”发布版本)开始,并在当年每次后续数据库更新时递增。
与其他转换后的版本标识符一样,相应的 Olson 数据库版本可以记录在项目元数据中。
版本说明符
版本说明符由一系列版本子句组成,用逗号分隔。例如:
~= 0.9, >= 1.0, != 1.3.4.*, < 2.0
比较运算符决定版本子句的类型:
逗号(“,”)等同于逻辑**与**运算符:候选版本必须匹配所有给定版本子句才能整体匹配说明符。
条件运算符和后续版本标识符之间的空格是可选的,逗号周围的空格也是可选的。
当多个候选版本匹配一个版本说明符时,首选版本应为由标准版本方案定义的一致排序所确定的最新版本。是否将预发布版本视为候选版本应按照预发布版本的处理中的描述进行处理。
除非下面特别注明,否则版本说明符中不得允许本地版本标识符,并且在检查候选版本是否匹配给定版本说明符时,本地版本标签必须完全忽略。
兼容版本
兼容发布子句由兼容发布运算符~=和版本标识符组成。它匹配任何预期与指定版本兼容的候选版本。
指定的版本标识符必须采用版本方案中描述的标准格式。此版本说明符中不允许使用本地版本标识符。
对于给定的发布标识符V.N,兼容发布子句近似等同于以下一对比较子句:
>= V.N, == V.*
此运算符不得用于单段版本号,例如~=1。
例如,以下几组版本子句是等效的:
~= 2.2
>= 2.2, == 2.*
~= 1.4.5
>= 1.4.5, == 1.4.*
如果在兼容发布子句中将预发布版本、后期发布版本或开发发布版本命名为V.N.suffix,则在确定所需的匹配前缀时会忽略后缀:
~= 2.2.post3
>= 2.2.post3, == 2.*
~= 1.4.5a4
>= 1.4.5a4, == 1.4.*
发布段比较的填充规则意味着兼容发布子句中假定的向前兼容性程度可以通过向版本说明符附加额外的零来控制:
~= 2.2.0
>= 2.2.0, == 2.2.*
~= 1.4.5.0
>= 1.4.5.0, == 1.4.5.*
版本匹配
版本匹配子句包括版本匹配运算符==和一个版本标识符。
指定的版本标识符必须采用版本方案中描述的标准格式,但允许在公共版本标识符后附加.*,如下所述。
默认情况下,版本匹配运算符基于严格的相等性比较:指定的版本必须与请求的版本完全相同。执行的*唯一*替换是发布段的零填充,以确保发布段以相同的长度进行比较。
严格版本匹配是否适用取决于版本说明符的具体用例。自动化工具至少应发出警告,并可能在不恰当地使用严格版本匹配时完全拒绝它们。
可以通过在版本匹配子句中的版本标识符后附加一个尾随的.*来请求前缀匹配而不是严格比较。这意味着在确定版本标识符是否匹配该子句时,将忽略额外的尾随段。如果指定的版本只包含一个发布段,则发布段中的尾随组件(或缺少尾随组件)也将被忽略。
例如,给定版本1.1.post1,以下子句将匹配或不匹配,如所示:
== 1.1 # Not equal, so 1.1.post1 does not match clause
== 1.1.post1 # Equal, so 1.1.post1 matches clause
== 1.1.* # Same prefix, so 1.1.post1 matches clause
为了进行前缀匹配,预发布段被认为有一个隐含的前导.,因此给定版本1.1a1,以下子句将匹配或不匹配,如所示:
== 1.1 # Not equal, so 1.1a1 does not match clause
== 1.1a1 # Equal, so 1.1a1 matches clause
== 1.1.* # Same prefix, so 1.1a1 matches clause if pre-releases are requested
精确匹配也被视为前缀匹配(此解释由版本标识符发布段的通常零填充规则暗示)。给定版本1.1,以下子句将匹配或不匹配,如所示:
== 1.1 # Equal, so 1.1 matches clause
== 1.1.0 # Zero padding expands 1.1 to 1.1.0, so it matches clause
== 1.1.dev1 # Not equal (dev-release), so 1.1 does not match clause
== 1.1a1 # Not equal (pre-release), so 1.1 does not match clause
== 1.1.post1 # Not equal (post-release), so 1.1 does not match clause
== 1.1.* # Same prefix, so 1.1 matches clause
包含开发版本或本地版本的前缀匹配,例如1.0.dev1.*或1.0+foo1.*,是无效的。如果存在,开发发布段始终是公共版本中的最后一个段,并且本地版本在比较时被忽略,因此在前缀匹配中使用两者都没有任何意义。
强烈不鼓励在为已发布的发行版定义依赖关系时使用==(至少不带通配符后缀),因为它极大地复杂化了安全修复的部署。严格版本比较运算符主要用于在使用共享发行版索引时定义*应用程序可重复部署*的依赖关系。
如果指定的版本标识符是公共版本标识符(无本地版本标签),则在匹配版本时必须忽略任何候选版本的本地版本标签。
如果指定的版本标识符是本地版本标识符,则在匹配版本时必须考虑候选版本的本地版本标签,其中公共版本标识符如上所述进行匹配,本地版本标签使用严格的字符串相等比较进行检查。
版本排除
版本排除子句包括版本排除运算符!=和一个版本标识符。
允许的版本标识符和比较语义与版本匹配运算符相同,只是任何匹配的含义都被反转。
例如,给定版本1.1.post1,以下子句将匹配或不匹配,如所示:
!= 1.1 # Not equal, so 1.1.post1 matches clause
!= 1.1.post1 # Equal, so 1.1.post1 does not match clause
!= 1.1.* # Same prefix, so 1.1.post1 does not match clause
包含式有序比较
包含式有序比较子句包括一个比较运算符和一个版本标识符,它将匹配任何版本,其中根据候选版本和指定版本在标准版本方案定义的一致排序中的相对位置,比较结果是正确的。
包含式有序比较运算符是<=和>=。
与版本匹配一样,发布段在必要时会用零填充,以确保发布段以相同的长度进行比较。
此版本说明符中不允许使用本地版本标识符。
排他式有序比较
排他性有序比较符>和<与包含性有序比较符类似,它们都依赖于候选版本和指定版本在标准版本方案定义的一致排序中的相对位置。然而,它们明确排除了指定版本的预发布版本、后期发布版本和本地版本。
排他性有序比较>V**不得**允许给定版本的后期发布版本,除非V本身是后期发布版本。您可以使用>V.postN来强制要求发布版本晚于特定后期发布版本,包括额外的后期发布版本。例如,>1.7将允许1.7.1但不允许1.7.0.post1,而>1.7.post2将允许1.7.1和1.7.0.post3但不允许1.7.0。
排他性有序比较>V**不得**匹配指定版本的本地版本。
排他式有序比较<V**不得**允许指定版本的预发布版本,除非指定版本本身是预发布版本。允许早于但不等于特定预发布版本的预发布版本可以通过使用<V.rc1或类似的方式来实现。
与版本匹配一样,发布段在必要时会用零填充,以确保发布段以相同的长度进行比较。
此版本说明符中不允许使用本地版本标识符。
任意相等性
任意相等比较是简单的字符串相等操作,不考虑任何语义信息,例如零填充或本地版本。此运算符也不支持像==运算符那样进行前缀匹配。
任意相等性的主要用例是允许指定无法通过本 PEP 表示的版本。此运算符是特殊的,充当一个应急出口,允许使用实现本 PEP 的工具的人仍然可以安装与本 PEP 不兼容的遗留版本。
一个例子是===foobar,它将匹配foobar的版本。
此运算符也可用于明确要求项目的未修补版本,例如===1.0,它将不匹配版本1.0+downstream1。
强烈不鼓励使用此运算符,工具在遇到时可能会显示警告。
预发布版本的处理
所有类型的预发布版本,包括开发发布版本,都被隐式排除在所有版本说明符之外,*除非*它们已存在于系统上,用户明确请求,或者满足版本说明符的唯一可用版本是预发布版本。
默认情况下,依赖解析工具应:
- 接受所有版本说明符的已安装预发布版本
- 接受远程可用的预发布版本,其中没有最终发布版本或后期发布版本满足版本说明符
- 将所有其他预发布版本排除在外
如果需要预发布版本才能满足版本说明符,依赖解析工具可以发出警告。
依赖解析工具还应允许用户请求以下替代行为:
- 接受所有版本说明符的预发布版本
- 排除所有版本说明符的预发布版本(如果预发布版本已本地安装,或者预发布版本是满足特定说明符的唯一方式,则报告错误或警告)
依赖解析工具还可以允许对上述行为进行按发行版控制。
后期发布版本和最终发布版本在版本说明符中没有特殊处理——它们总是被包含,除非明确排除。
示例
~=3.1:3.1 版或更高版本,但不是 4.0 版或更高版本。~=3.1.2:3.1.2 版或更高版本,但不是 3.2.0 版或更高版本。~=3.1a1:3.1a1 版或更高版本,但不是 4.0 版或更高版本。== 3.1:具体指 3.1 版(或 3.1.0),排除所有预发布版本、后期发布版本、开发发布版本和任何 3.1.x 维护发布版本。== 3.1.*:以 3.1 开头的任何版本。等同于~=3.1.0兼容发布子句。~=3.1.0, != 3.1.3:3.1.0 版或更高版本,但不是 3.1.3 版,也不是 3.2.0 版或更高版本。
直接引用
一些自动化工具可能允许使用直接引用作为常规版本说明符的替代。直接引用由说明符@和一个显式 URL 组成。
直接引用是否合适取决于版本说明符的具体用例。自动化工具至少应发出警告,并在不恰当地使用直接引用时可以完全拒绝它们。
公共索引服务器不应允许在上传的发行版中使用直接引用。直接引用是为软件集成商而不是出版商设计的工具。
根据用例,直接 URL 引用的某些适当目标可以是 sdist 或 wheel 二进制档案。支持的确切 URL 和目标将取决于工具。
例如,可以直接引用本地源档案:
pip @ file:///localbuilds/pip-1.3.1.zip
或者,也可以引用预构建的档案:
pip @ file:///localbuilds/pip-1.3.1-py33-none-any.whl
所有不引用本地文件 URL 的直接引用都应指定安全的传输机制(例如https),并且在 URL 中包含预期哈希值以进行验证。如果直接引用未指定任何哈希信息,或哈希信息工具无法理解,或所选哈希算法工具认为信任度太弱,自动化工具至少应发出警告,并可能拒绝依赖该 URL。如果此类直接引用还使用不安全的传输,自动化工具不应依赖该 URL。
建议仅使用标准库hashlib模块最新版本无条件提供的哈希值作为源档案哈希值。截至撰写本文时,该列表包括'md5'、'sha1'、'sha224'、'sha256'、'sha384'和'sha512'。
对于源档案和 wheel 引用,可以通过在 URL 片段中包含<hash-algorithm>=<expected-hash>条目来指定预期哈希值。
对于版本控制引用,应使用VCS+protocol方案来同时标识版本控制系统和安全传输,并且应使用具有基于哈希的提交标识符的版本控制系统。自动化工具可以省略对不提供基于哈希的提交标识符的版本控制系统缺少哈希值的警告。
为了处理不支持直接在 URL 中包含提交或标签引用的版本控制系统,该信息可以使用@<commit-hash>或@<tag>#<commit-hash>表示法附加到 URL 的末尾。
注意
这与 pip 当前支持的 VCS 引用表示法**不完全**相同。首先,发行版名称被移到前面而不是嵌入到 URL 中。其次,即使基于标签进行检索,也包含提交哈希,以满足上述要求,即**每个**链接都应包含哈希,以使伪造更加困难(创建一个带有特定标签的恶意仓库很容易,创建一个带有特定**哈希**的仓库则不然)。
远程 URL 示例:
pip @ https://github.com/pypa/pip/archive/1.3.1.zip#sha1=da9234ee9982d4bbb3c72346a6de940a148ea686
pip @ git+https://github.com/pypa/pip.git@7921be1537eac1e97bc40179a57f0349c2aee67d
pip @ git+https://github.com/pypa/pip.git@1.3.1#7921be1537eac1e97bc40179a57f0349c2aee67d
文件 URL
文件 URL 的形式为file://<host>/<path>。如果省略<host>,则假定为localhost,即使省略<host>,第三个斜杠也必须存在。<path>定义了要访问的文件系统上的文件路径。
在各种 *nix 操作系统上,<host>唯一允许的值是省略它,localhost,或当前机器认为与其自己的主机匹配的另一个 FQDN。换句话说,在 *nix 上,file://方案只能用于访问本地机器上的路径。
在 Windows 上,文件格式应在<path>中包含驱动器盘符(如果适用)(例如file:///c:/path/to/a/file)。与 *nix 不同,在 Windows 上,<host>参数可用于指定网络共享上的文件。换句话说,为了将\\machine\volume\file转换为file://url,它将变为file://machine/volume/file。有关 Windows 上file://URL 的更多信息,请参阅 MSDN[4]。
更新版本规范
版本规范可以通过澄清进行更新,而无需新的 PEP 或元数据版本更改。
任何影响版本识别和比较语法和语义的技术更改都将需要在一个新的 PEP 中定义更新的版本方案。
与 pkg_resources.parse_version 的区别总结
- 注:此比较是与编写 PEP 时存在的
pkg_resourses.parse_version进行比较的。PEP 被接受后,setuptools 6.0 及更高版本采用了本 PEP 中描述的行为。 - 本地版本的排序方式不同,本 PEP 要求它们排序为大于没有本地版本的相同版本,而
pkg_resources.parse_version将其视为预发布标记。 - 本 PEP 特意限制了构成有效版本的语法,而
pkg_resources.parse_version试图从*任何*任意字符串中提供一些含义。 pkg_resources.parse_version允许任意深度嵌套的版本指示符,例如1.0.dev1.post1.dev5。然而,本 PEP 只允许每种类型使用一次,并且它们必须以特定顺序存在。
与 PEP 386 的区别总结
- 将版本说明符的描述移至版本控制 PEP 中。
- 添加了“直接引用”概念作为资源直接引用的标准表示法(而不是每个工具都需要发明自己的表示法)。
- 添加了“本地版本标识符”和“本地版本标签”概念,允许系统集成商以受上游工具支持的方式指示修补构建,并允许将构建标签并入二进制发行版的版本控制中。
- 添加了“兼容发布”子句
- 添加了用于基于前缀的版本匹配和排除的尾随通配符语法。
- 改变了
.devN后缀的顶级排序位置 - 允许单值版本号
- 明确排除前导或尾随空格
- 明确支持基于日期的版本
- 明确的规范化规则,以提高与 PyPI 上现有版本元数据的兼容性,前提是不引入歧义。
- 隐式排除预发布版本,除非它们已存在或需要满足依赖关系
- 以与无限定发布版本相同的方式处理后期发布版本
- 讨论元数据版本之间的排序和依赖关系
- 从偏好
c改为偏好rc。
重大更改的理由在以下各节中给出。
更改版本方案
本 PEP 中版本方案相对于PEP 386的一个关键变化是,将顶级开发发布版本(如X.Y.devN)排序在 alpha 发布版本(如X.Ya1)之前。这是一种更具逻辑性的排序顺序,因为已经同时使用开发发布版本和 alpha/beta/发布候选版本的项目不希望它们的开发发布版本排序在发布候选版本和最终发布版本之间。在这种位置使用dev发布版本而不是仅仅创建额外的发布候选版本是没有道理的。
更新后的排序顺序也意味着dev版本的排序在元数据标准和pkg_resources的预先存在行为(以及当前安装工具的行为)之间保持一致。
进行此更改应使受影响的现有项目更容易迁移到最新版本的元数据标准。
版本方案的另一个变化是允许使用单个数字版本,类似于 Mozilla Firefox、Google Chrome 和 Fedora Linux 发行版等非 Python 项目所使用的版本。这实际上预计对版本说明符更有用,但同时允许版本说明符和发布编号使用它比拆分两个定义更容易。
在 PyPI 上发现几个版本标识符仅因尾随的\n字符而不同的项目后,明确排除了前导和尾随空格。
如后面单独的版本规范化部分所述,还添加了各种其他规范化规则。
附录 A显示了对 PyPI 发行版版本信息(截至 2014 年 8 月 8 日收集)的详细分析结果。此分析比较了本 PEP 中明确定义的有序版本方案与 setuptools 行为定义的实际标准之间的行为。这些指标很有用,因为本 PEP 的目的是尽可能密切地遵循现有 setuptools 的行为,同时仍对无法排序的版本抛出异常(而不是像 setuptools 那样试图猜测合适的顺序)。
关于版本方案更主观的描述
与PEP 386一样,主要关注点是**将现有实践编码**,使其更适合自动化,而不是要求现有项目对其工作流程进行非平凡的更改。然而,标准方案提供了比绝大多数简单 Python 包所需的灵活性大得多的灵活性(这些包通常甚至不需要维护发布版本——许多用户很乐意通过升级到新功能发布版本来获取错误修复)。
为了方便新手开发者以及希望更好地理解各种用例的经验丰富的开发者,本规范现在更详细地阐述了所定义版本方案的组成部分,包括每个组件在实践中如何使用的示例。
PEP 还明确指导开发者采用(但不强制要求)语义版本控制,并劝退使用完整版本控制方案中的某些方面,这些方面在很大程度上是为了涵盖现有项目实践和为 Linux 发行版重新打包软件中的一些深奥的边缘情况。
与版本方案一起描述版本说明符
首先,拥有标准化版本方案的主要原因是使其更容易进行可靠的自动化依赖分析。在版本标识符的定义旁边描述其主要用例更有意义。
更改版本说明符的解释
以前对版本说明符的解释使得意外下载依赖项的预发布版本变得非常容易。这反过来又使得开发者难以向 Python 包索引发布软件的预发布版本,因为即使将包标记为隐藏也不足以阻止自动化工具下载它,并且还使得用户通过主 PyPI Web 界面手动获取测试版本变得更加困难。
以前的解释还无充分理由地将后期发布排除在某些版本说明符之外。
更新后的解释旨在使意外接受预发布版本作为满足依赖项变得困难,同时在预发布版本是满足依赖项的唯一方式时,仍然允许自动检索预发布版本。
“假设存在某种前向兼容性”版本约束来源于 Ruby 社区的“悲观版本约束”运算符 [2],以允许项目对前向兼容性承诺采取谨慎态度,同时仍然轻松设置其依赖项的最低所需版本。兼容版本子句 (~=) 的拼写受到 Ruby (~>) 和 PHP (~) 等价物的影响。
还计划进一步改进同一库多个版本的并行安装处理,但这将取决于安装数据库定义的更新以及动态路径操作工具的改进。
添加了尾部通配符语法以请求基于前缀的版本匹配,以便能够合理地定义兼容版本子句。
支持基于日期的版本标识符
排除基于日期的版本导致在将 pytz 迁移到新元数据标准时出现重大问题。这也引起了 OpenStack 开发者的担忧,因为他们使用基于日期的版本控制方案,并希望能够在不更改它的情况下迁移到新的元数据标准。
添加版本纪元
添加版本纪元的原因与其他版本控制方案(例如 Fedora 和 Debian Linux 发行版)的原因相同:允许项目优雅地更改其发布编号方法,而无需新版本显示具有比以前版本更低的版本号,也无需更改项目名称。
特别是,支持版本纪元允许以前使用基于日期的版本控制的项目通过指定新的版本纪元切换到语义版本控制。
选择字符 ! 来分隔纪元版本,而不是其他系统中常用的字符 :,原因在于 : 在 Windows 目录名中不是有效字符。
添加直接引用
添加直接引用作为“逃逸条款”,以处理不完全符合标准分发模型的复杂实际情况。这包括对内部使用的未发布软件的依赖,以及处理在将第三方库包装为 C 扩展时可能出现的更复杂的兼容性问题(这尤其受到科学界的关注)。
索引服务器被特意赋予了很大的自由来禁止直接引用,因为它们主要用作集成商的工具而不是发布商的工具。特别是 PyPI 目前正在进行消除对外部引用的依赖的过程,因为不可靠的外部服务会减慢安装操作,并降低 PyPI 自身的明显可靠性。
添加任意相等性
添加任意相等性作为“逃逸条款”,以处理需要安装使用不符合版本项目的情况。尽管此 PEP 能够实现与 PyPI 上已有的版本约 97% 的兼容性,但仍有约 3% 的版本无法解析。此运算符提供了一种简单有效的方法,仍然可以依赖它们,而无需“猜测”它们的语义(如果支持的不是严格的基于字符串的相等性,则需要这样做)。
添加本地版本标识符
下游集成商通常需要将上游错误修复程序回溯到旧版本,这是生活中的一个事实。这是 Linux 发行版供应商获得报酬的服务之一,应用程序开发者也可能将他们需要的补丁应用于捆绑的依赖项。
历史上,这种做法对于跨平台语言特定的分发工具是不可见的——上游元数据中报告的“版本”与未修改代码的版本相同。这种不准确性可能在尝试混合使用集成商提供的代码和未修改的上游代码时,或者甚至只是尝试准确识别安装了哪个版本的软件时导致问题。
在版本方案中引入本地版本标识符和“本地版本标签”,以及相应的 python.integrator 元数据扩展,使得这种活动能够被准确表示,这应该能改善上游工具和各种集成平台之间的互操作性。
所选择的具体方案主要基于 pkg_resources.parse_version 和 pkg_resources.parse_requirements 的现有行为,主要区别在于,pkg_resources 目前在比较版本以进行精确匹配时总是将后缀考虑在内,而 PEP 要求在版本说明符子句中没有本地版本标签时,忽略候选版本的本地版本标签。此外,PEP 不试图对本地版本标签施加任何结构(除了限制允许的字符集和定义它们的排序)。
此更改旨在确保集成商提供的版本,例如 pip 1.5+1 或 pip 1.5+1.git.abc123de 仍然能满足像 pip>=1.5 这样的版本说明符。
选择加号主要是为了提高本地版本标识符的可读性。选择它而不是连字符是为了防止 pkg_resources.parse_version 将其解析为预发布版本,这对于成功迁移到新的、更结构化的版本控制方案至关重要。选择加号而不是波浪号是因为波浪号在 Debian 的版本排序算法中的重要性。
提供明确的版本规范化规则
历史上,Python 中解析版本的实际标准是 setuptools 项目中的 pkg_resources.parse_version 命令。它不试图拒绝任何版本,而是尝试从给定的一切中生成一些有意义的东西,成功程度各不相同。它有一些简单的规则,但除此之外,它或多或少地主要依赖于字符串比较。
本 PEP 中提供的规范化规则主要旨在提高与 pkg_resources.parse_version 的兼容性,特别是在 rev、r、pre 等已记录的用例中,或者对 PyPI 上已存在的版本进行更合理的处理。
所有可能的规范化规则都根据它们是否可能导致任何歧义进行了权衡(例如,虽然有人可能会设计一个方案,其中 v1.0 和 1.0 被认为是不同的版本,但任何人实际这样做的可能性,更不用说在任何可察觉的规模上,都相当低)。它们还根据 pkg_resources.parse_version 如何处理特定版本字符串,特别是在排序方面进行了权衡。最后,每条规则都根据它允许的附加版本类型、这些版本看起来有多“丑陋”、解析难度(无论是心理上还是机械上)以及它将带来多少额外的兼容性进行了权衡。
可能的规范化范围限制在可以作为版本解析的一部分轻松实现的事情,而不是应用于版本预解析转换。这样做是为了限制每个转换的副作用,因为简单的搜索和替换样式转换会增加歧义或“垃圾”版本的可能性。
允许下划线进行规范化
PyPI 上使用版本字符串中下划线 _ 的项目不多。然而,本 PEP 允许在连字符 - 可接受的任何地方使用下划线。原因是 Wheel 规范化方案规定连字符 - 被规范化为下划线 _,以便更容易解析文件名。
PEP 440 的变更总结
根据 setuptools 8.0 和 pip 6.0 中初始参考实现发布后收到的反馈,对本 PEP 进行了以下更改
- 独占有序比较已更新,不再暗示
!=V.*,这被认为是一种令人惊讶的行为,难以准确描述。相反,独占有序比较将简单地禁止匹配指定版本的预发布版本、后期发布版本和本地版本(除非指定版本本身是预发布版本、后期发布版本或本地版本)。有关扩展讨论,请参阅 distutils-sig 上的帖子 [6] [7]。 - 候选版本的规范化形式已从“c”更新为“rc”。此更改是基于 setuptools 8.0 开始对准备在 PyPI 上发布的包时生成的发布元数据应用规范化时收到的用户反馈 [8]。
- PEP 文本和
is_canonical正则表达式已更新,明确规定数字组件必须特别表示为 ASCII 数字序列,而不是任意 Unicode [Nd] 码点。这以前在附录 B 中的版本解析正则表达式中有所暗示,但没有明确说明 [10]。
参考资料
最初标准化版本方案的尝试,以及需要此类标准的理由,可在 PEP 386 中找到。
附录 A
Metadata v2.0 指南与 setuptools
$ invoke check.pep440
Total Version Compatibility: 245806/250521 (98.12%)
Total Sorting Compatibility (Unfiltered): 45441/47114 (96.45%)
Total Sorting Compatibility (Filtered): 47057/47114 (99.88%)
Projects with No Compatible Versions: 498/47114 (1.06%)
Projects with Differing Latest Version: 688/47114 (1.46%)
附录 B:使用正则表达式解析版本字符串
如前文 Public version identifiers 部分所述,已发布的版本标识符应使用规范格式。本节提供可用于测试版本是否已采用该格式的正则表达式,如果不是,则提取各个组件以进行后续规范化。
要测试版本标识符是否为规范格式,可以使用以下函数
import re
def is_canonical(version):
return re.match(r'^([1-9][0-9]*!)?(0|[1-9][0-9]*)(\.(0|[1-9][0-9]*))*((a|b|rc)(0|[1-9][0-9]*))?(\.post(0|[1-9][0-9]*))?(\.dev(0|[1-9][0-9]*))?$', version) is not None
要提取版本标识符的组件,请使用以下正则表达式(由 packaging 项目定义)
VERSION_PATTERN = r"""
v?
(?:
(?:(?P<epoch>[0-9]+)!)? # epoch
(?P<release>[0-9]+(?:\.[0-9]+)*) # release segment
(?P<pre> # pre-release
[-_\.]?
(?P<pre_l>alpha|a|beta|b|preview|pre|c|rc)
[-_\.]?
(?P<pre_n>[0-9]+)?
)?
(?P<post> # post release
(?:-(?P<post_n1>[0-9]+))
|
(?:
[-_\.]?
(?P<post_l>post|rev|r)
[-_\.]?
(?P<post_n2>[0-9]+)?
)
)?
(?P<dev> # dev release
[-_\.]?
(?P<dev_l>dev)
[-_\.]?
(?P<dev_n>[0-9]+)?
)?
)
(?:\+(?P<local>[a-z0-9]+(?:[-_\.][a-z0-9]+)*))? # local version
"""
_regex = re.compile(
r"^\s*" + VERSION_PATTERN + r"\s*$",
re.VERBOSE | re.IGNORECASE,
)
版权
本文档已置于公共领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0440.rst
最后修改时间:2025-02-01 08:55:40 GMT