版本说明符#
本规范描述了一种用于识别 Python 软件分发版本以及声明对特定版本依赖关系的方案。
定义#
本文档中的关键词“必须”、“不得”、“必需”、“应”、“不应”、“应当”、“不应当”、“建议”、“可以”和“可选”应按 RFC 2119 中的描述进行解释。
“构建工具”是指旨在运行在开发系统上的自动化工具,用于生成源和二进制分发存档。集成工具也可以调用构建工具,以便构建作为 sdist 而不是预构建二进制存档分发的软件。
“索引服务器”是活动分发注册表,用于发布版本和依赖关系元数据,并对允许的元数据施加约束。
“发布工具”是指旨在运行在开发系统上并上传源和二进制分发存档到索引服务器的自动化工具。
“安装工具”是专门旨在运行在部署目标上的集成工具,用于从索引服务器或其他指定位置获取源和二进制分发存档,并将它们部署到目标系统。
“自动化工具”是一个总称,涵盖构建工具、索引服务器、发布工具、集成工具以及任何其他生成或使用分发版本和依赖关系元数据的软件。
版本方案#
分发由一个公共版本标识符标识,该标识符支持所有已定义的版本比较操作
版本方案用于描述特定分发存档提供的分发版本,以及对构建或运行软件所需的依赖关系版本施加约束。
公共版本标识符#
规范的公共版本标识符必须符合以下方案
[N!]N(.N)*[{a|b|rc}N][.postN][.devN]
公共版本标识符不得包含前导或尾随空格。
公共版本标识符在给定分发中必须唯一。
安装工具应忽略任何不符合此方案的公共版本,但还必须包括下面指定的规范化。安装工具在检测到不兼容或不明确的版本时可以向用户发出警告。
另请参阅 附录:使用正则表达式解析版本字符串,其中提供了一个正则表达式来检查与规范格式的严格一致性,以及一个更宽松的正则表达式,接受可能需要后续规范化的输入。
公共版本标识符被分成最多五个部分
纪元部分:
N!
发行部分:
N(.N)*
预发行部分:
{a|b|rc}N
后发行部分:
.postN
开发发行部分:
.devN
任何给定的发行都将是“最终发行”、“预发行”、“后发行”或“开发发行”,如以下部分中所定义。
所有数字组件都必须是非负整数,表示为 ASCII 数字序列。
所有数字组件必须根据其数字值进行解释和排序,而不是作为文本字符串。
所有数字组件可以为零。除了以下针对发布段落所述的情况外,零数字组件没有任何特殊意义,除了始终是版本排序中的最低可能值。
注意
为了更好地适应现有公共和私有 Python 项目中广泛的版本控制实践,此方案允许使用一些难以读取的版本标识符。
因此,强烈建议不要在新项目中使用规范在技术上允许的某些版本控制实践。如果是这种情况,则在以下部分中会注明相关详细信息。
本地版本标识符#
本地版本标识符必须符合以下方案
<public version identifier>[+<local version label>]
它们由一个常规公共版本标识符(如上一部分中所定义)以及一个任意“本地版本标签”组成,该标签由加号与公共版本标识符分隔。本地版本标签没有分配任何特定语义,但会施加一些语法限制。
本地版本标识符用于表示上游项目完全兼容的 API(如果适用,还兼容 ABI)修补版本。例如,当升级到新的上游版本会对应用程序或其他集成系统(例如 Linux 发行版)造成太大破坏时,应用程序开发人员和系统集成商可以通过应用特定的反向移植错误修复来创建这些版本。
包含本地版本标签可以区分上游版本和下游集成商可能更改的重新构建。使用本地版本标识符不会影响发布的类型,但当应用于源发行版时,确实表示它可能不包含与相应上游版本完全相同的代码。
为了确保本地版本标识符可以轻松地作为文件名和 URL 的一部分进行合并,并避免十六进制哈希表示中的格式不一致,本地版本标签必须限制在以下允许字符集中
ASCII 字母 (
[a-zA-Z]
)ASCII 数字 (
[0-9]
)句点 (
.
)
本地版本标签必须以 ASCII 字母或数字开头和结尾。
本地版本的比较和排序分别考虑本地版本(由 .
分隔)的每个段落。如果一个段落完全由 ASCII 数字组成,那么在比较时应将该部分视为整数,如果一个段落包含任何 ASCII 字母,那么该段落将不区分大小写地按词典顺序进行比较。在比较数字段落和词法段落时,数字段落始终被比较为大于词法段落。此外,具有大量段落的本地版本始终被比较为大于具有较少段落的本地版本,只要较短本地版本的段落与较长本地版本的段落的开头完全匹配。
“上游项目”是指定义其自己的公共版本的项目。“下游项目”是指跟踪和重新分发上游项目的一个项目,可能会从上游项目的更高版本反向移植安全性和错误修复。
在将上游项目发布到公共索引服务器时,不应使用本地版本标识符,但可以使用本地版本标识符来识别直接从项目源创建的私有构建。在下游项目发布与公共版本标识符识别的上游项目版本兼容但包含其他更改(例如错误修复)的版本时,应使用本地版本标识符。由于 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
的相同版本被视为等效。
前导和尾随空白#
前导和尾随空白必须被静默忽略,并从版本的所有标准化形式中删除。这包括 " "
、\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 发行版开发人员。
版本标识符的 epoch 段必须根据给定 epoch 的数值进行排序。如果不存在 epoch 段,则隐式数值为 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
的情况,因为这会产生歧义,并且仍然符合规范。
在 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 386,即使对于元数据 v1.2 也是如此。工具应忽略无法按此规范中的规则解析的任何版本,但如果没有任何符合此规范的版本可用,则可能会回退到实现定义的版本解析和排序方案。
发行版用户可能希望从他们控制的任何私有软件包索引中明确删除不兼容的版本。
与其他版本方案的兼容性#
一些项目可能会选择使用需要转换才能符合此规范中定义的公共版本方案的版本方案。在这种情况下,项目特定版本可以存储在元数据中,而转换后的公共版本则发布在版本字段中。
这允许自动发行工具对已发布版本提供始终正确的排序,同时仍允许开发人员为其项目使用他们喜欢的内部版本控制方案。
语义版本控制#
语义版本控制 是一种流行的版本标识方案,它比此规范更具规范性,规定了发布号不同元素的意义。即使项目选择不遵守语义版本控制的细节,该方案也值得理解,因为它涵盖了许多在依赖其他发行版时可能出现的问题,以及在发布其他人依赖的发行版时可能出现的问题。
语义版本控制的“主版本.次版本.修订版本”(在此规范中描述为“主版本.次版本.微版本”)方面(2.0.0 规范中的第 1-8 条)与本规范中定义的版本方案完全兼容,并且鼓励遵守这些方面。
包含连字符(预发布 - 条款 10)或加号(构建 - 条款 11)的语义版本不与此规范兼容,且不允许在公共版本字段中使用。
将此类基于语义版本控制的源标签转换为兼容的公共版本的一种可能机制是使用 .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
或类似方法来允许早于但等于特定预发行版的预发行版。
与版本匹配一样,发行版部分会根据需要进行零填充,以确保发行版部分以相同长度进行比较。
此版本说明符中不允许使用本地版本标识符。
任意相等#
任意相等比较是简单的字符串相等操作,不考虑任何语义信息,例如零填充或本地版本。此运算符也不支持前缀匹配,就像 ==
运算符一样。
任意相等的用例主要是允许指定无法用此说明表示的版本。此运算符是特殊的,充当一个逃生舱口,允许使用实现此说明的工具的人员仍然安装与此说明不兼容的旧版本。
一个示例是 ===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 引用,可以通过将 <hash-algorithm>=<expected-hash>
条目作为 URL 片段的一部分来指定预期哈希值。
对于版本控制引用,应使用 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。
与 pkg_resources.parse_version 的差异总结#
注意:此比较针对
pkg_resourses.parse_version
,当时 PEP 440 编写。在 PEP 被接受后,setuptools 6.0 及更高版本采用了此处描述的行为。本地版本排序不同,本规范要求它们按大于没有本地版本的相同版本进行排序,而
pkg_resources.parse_version
则将其视为预发布标记。本规范故意限制构成有效版本的语法,而
pkg_resources.parse_version
尝试从任何任意字符串中提供一些含义。pkg_resources.parse_version
允许任意深度嵌套的版本标识符,如1.0.dev1.post1.dev5
。然而,本规范只允许每种类型使用一次,并且它们必须按特定顺序存在。
附录:使用正则表达式解析版本字符串#
如前文 公共版本标识符 部分所述,已发布的版本标识符应使用规范格式。本部分提供了可用于测试版本是否已采用该格式的正则表达式,如果尚未采用,则可用于提取各个组件以便后续规范化。
要测试版本标识符是否采用规范格式,可以使用以下函数
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>(a|b|c|rc|alpha|beta|pre|preview))
[-_\.]?
(?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,
)
历史记录#
2014 年 8 月:此规范通过 PEP 440 批准。