PEP 598 – 引入增量功能发布
- 作者:
- Alyssa Coghlan <ncoghlan at gmail.com>
- 讨论主题:
- Discourse 讨论主题
- 状态:
- 已撤回
- 类型:
- 信息性
- 创建日期:
- 2019 年 6 月 15 日
- Python 版本:
- 3.9
摘要
PEP 602 提出通过将 CPython 功能发布的频率从每 18-24 个月增加到每 9-12 个月来减少 Python 标准库和 CPython 参考解释器的功能交付延迟。
此 PEP 提出将新的基线功能发布(以及相关的文件系统布局更改、字节码格式更改和 C ABI 兼容性中断)的频率降低到每两年发布一次(2020 年、2022 年、2024 年等),但将此更改与新的策略和方法相结合,该策略和方法允许在给定发布系列的初始点发布集中引入向后兼容的功能。
PEP 撤回
此 PEP 已被撤回,取而代之的是 PEP 605 中的滚动 beta 发布流提案。
但是,此 PEP 中提出的问题很可能适用于任何其他允许功能回滚以改善支持此类发布的开发者体验的“长期支持分支”提案(例如 [3] 中的 EL Python 草案),因此此处介绍的想法可以为这类提案提供有用的设计建议。
概述
保持当前的 CPython 发布兼容性管理流程,但更频繁地进行此流程的提议存在重大的实际缺点,因为 CPython 功能发布带有一些期望(最重要的是,5 年的维护生命周期,支持与先前功能发布并行安装,以及 CPython 特定的 ABI 可能发生的重大更改,需要重新编译所有扩展模块),这意味着更快的功能发布以其当前形式有可能大大增加维护所有活动支持的 CPython 发布版本上的第三方 Python 库和应用程序的负担。
对于这种方法是否会显著减少大多数最终用户在实践中典型的功能交付延迟也存在争议,因为新功能发布的采用周期通常以月或年为单位,因此更频繁的发布可能只会导致最终用户更新到每 3 或 4 个功能发布,而不是每 2 或 3 个功能发布(就像今天经常发生的那样)。
此 PEP 提出了一种竞争性的提案,即将更改文件系统布局和 CPython ABI 的可并行安装功能发布的频率降低到一致的 24 个月周期,但通过引入构建兼容增量功能发布的概念来弥补这一点,然后将给定功能发布系列的完整功能冻结从初始基线 X.Y.0 发布延迟到后续的 X.Y.Z 功能完整发布,该发布在初始基线功能发布约 12 个月后发生。
一个新的 feature_complete
属性将添加到 sys.version_info
结构中,以提供一个程序性指示,说明发布系列是否仍对进一步的增量功能发布开放。Python 的其他实现也可以自由清除此标志,以指示它们对名义 Python 版本的支持可能仍在进行中。
出于兼容性测试目的,以及为了在混合版本环境中维护 pickle 兼容性,一个新的 sys.feature_limit
属性(以及相应的 CPython CLI 参数,--feature-limit X.Y.Z
和环境变量,PYTHONFEATURELIMIT
)将提供一种方法来限制增量功能发布中添加的功能在运行时的可用性。
现有周期和新周期将在功能冻结发布日期同步,因此 Python 3.9.x 的完整功能冻结将在 2021 年 10 月进行,即 Python 3.8.0 功能发布后的 24 个月,而初始的 Python 3.9.0 发布将在 2020 年 10 月进行。
未来发布计划示例
根据此提案,Python 3.9.0a1 将在 2019 年 11 月发布,紧随 2019 年 10 月的 Python 3.8.0 功能完整发布。
然后 3.9.0b1 发布将在 6 个月后的 2020 年 5 月进行,3.9.0 本身将在 2020 年 10 月发布。
假设 3.9.x 的微版本每季度发布一次,那么整体发布时间线将如下所示:
- 2019-11: 3.9.0a1
- …由发布经理确定的其他 alpha 版本
- 2020-05: 3.9.0b1
- …由发布经理确定的其他 beta 版本
- 2020-08: 3.9.0bX(锁定 ABI 兼容性的最终 beta 版本)
- 2020-09: 3.9.0rc1
- …由发布经理确定的其他候选版本
- 2020-10: 3.9.0(BFR - 基线功能发布)
- 2021-01: 3.9.1(IFR - 增量功能发布)
- 2021-04: 3.9.2(IFR)
- 2021-07: 3.9.3(IFR)
- 2021-10: 3.9.4(功能完整发布)
- 2022-01: 3.9.5
- 2022-04: 3.9.6
- 2022-07: 3.9.7
- 2022-10: 3.9.8(最终常规维护发布)
- …根据需要发布其他仅安全修复版本
- 2025-10: 3.9.x 分支关闭
功能完整发布版本号通常不带任何限定符(就像今天一样),而基线和增量功能发布预计将附加一个限定符,表明它们不是传统的 CPython 发布(3.9.0 (BFR)
,3.9.1 (IFR)
等)。
Python 3.10 发布系列将在第一个 Python 3.9 功能完整发布后的月份开始发布,与最终的 12 个月的 Python 3.9 常规维护发布并行。
- 2021-11: 3.10.0a1
- …由发布经理确定的其他 alpha 版本
- 2022-05: 3.10.0b1
- …由发布经理确定的其他 beta 版本
- 2022-08: 3.10.0bX(锁定 ABI 兼容性的最终 beta 版本)
- 2022-09: 3.10.0rc1
- …由发布经理确定的其他候选版本
- 2022-10: 3.10.0(BFR)
- 2023-01: 3.10.1(IFR)
- 2023-04: 3.10.2(IFR)
- 2023-07: 3.10.3(IFR)
- 2023-10: 3.10.4
- 2024-01: 3.10.5
- 2024-04: 3.10.6
- 2024-07: 3.10.7
- 2024-10: 3.10.8(最终常规维护发布)
- …根据需要发布其他仅安全修复版本
- 2027-10: 3.10.x 分支关闭
在此模型中,始终有两个或三个活动分支
- 2019-04 -> 2019-10: 3.9.0 预发布版,3.8.0 预发布版,3.7.x 维护版
- 2019-10 -> 2020-05: 3.9.0 预发布版,3.8.x 维护版
- 2020-05 -> 2020-10: 3.10.0 预发布版,3.9.0 预发布版,3.8.x 维护版
- 2020-10 -> 2021-10: 3.10.0 预发布版,3.9.x 功能发布,3.8.x 维护版
- 2021-10 -> 2022-05: 3.10.0 预发布版,3.9.x 维护版
- 2022-05 -> 2022-10: 3.11.0 预发布版,3.10.0 预发布版,3.9.x 维护版
- 2022-10 -> 2023-10: 3.11.0 预发布版,3.10.x 功能发布,3.9.x 维护版
- 2023-10 -> 2024-05: 3.11.0 预发布版,3.10.x 维护版
- 2024-05 -> 2024-10: 3.12.0 预发布版,3.11.0 预发布版,3.10.x 维护版
- …等等
(预发布版和预发布版开发在主 git 分支上进行,所有其他开发都在特定于发布的分支上进行,更改通常从主 git 分支回滚)
待办事项:这确实需要一个图表来帮助解释它,所以一旦我有一个要添加的图片,我就会添加。
这与现状非常相似,但节奏更加一致,在基线功能发布年份(2020 年、2022 年等)之间交替进行,重点是新基线功能发布的 alpha 和 beta 周期(同时继续发布先前功能发布系列的维护版本),以及功能完整发布年份(2021 年、2023 年等),重点是对当前功能发布系列进行较小的改进(同时为下一年的下一个功能发布系列做计划)。
提案
不包括 alpha 和 beta 版本,CPython 目前有 3 种不同的发布增量
- 功能发布(即 X.Y.0 发布)
- 维护发布(X.Y.0 发布后的约 2 年内发布的 X.Y.Z 发布)
- 仅源代码的安全发布(后续的 X.Y.Z 发布)
功能冻结在 X.Y.0b1 发布时进行。构建兼容性冻结现在在最后一个 beta 版本发布时进行(为项目在第一个候选版本发布之前将 wheel 档案上传到 PyPI 提供时间)。
然后,这在发布系列的生命周期中创建以下时期
- 预发布版(发布系列是 CPython 开发分支)
- beta 版(发布进入维护模式,ABI 兼容性基本锁定)
- 维护版(ABI 已锁定,仅接受错误修复和文档增强)
- 仅安全修复(不再发布二进制版本,仅接受安全修复)
- 生命周期结束(不再发布任何版本)
此 PEP 中的提案是将“功能发布”类别分成三种不同的功能发布
- 基线功能发布(X.Y.0 发布)
- 增量功能发布(基线功能发布和相应的功能完整发布之间发布的任何 X.Y.Z 发布)
- 功能完整发布(在 X.Y.0 发布后约 1 年发布的特定 X.Y.Z 发布)
- 维护发布(功能完整发布后的约 1 年内发布的 X.Y.Z 发布)
- 仅源代码的安全发布(后续的
X.Y.Z
发布)
然后,这将引入发布系列生命周期中的一个新“功能发布”阶段
- 预发布版(发布系列是 CPython 开发分支)
- beta 版(发布进入功能添加模式,ABI 兼容性尚未锁定)
- 功能发布(ABI 已锁定,接受向后兼容的 API 添加)
- 维护版(ABI 已锁定,仅接受错误修复和文档增强)
- 仅安全修复(不再发布二进制版本,仅接受安全修复)
- 生命周期结束(不再发布任何版本)
预发布版 beta 时期将放松,使用增量功能发布策略进行更改,而不是更严格的维护发布策略。
出于治理目的,基线功能发布是唯一符合 PEP 13 意义上的“功能发布”的发布(增量功能发布不算)。
基线功能发布和功能发布系列
基线功能发布本质上只是现有的功能发布,只是给了一个新名称,以帮助将其与新的增量功能发布区分开来,以及帮助表明与它们的先前版本不同,它们在发布时不再被视为功能完整。
基线功能版本将继续定义新的功能版本系列,锁定该系列剩余部分的以下语言、构建和安装兼容性约束
- Python 语言语法
ast
模块 AST 格式- CPython 解释器操作码格式
pyc
文件魔数和文件名兼容性标签- 扩展模块文件名兼容性标签
- 轮子档案兼容性标签
- 默认包和模块导入目录
- 默认安装文件名和目录
基线功能版本还将继续成为唯一可以引入以下内容的版本:
- 新的弃用、待弃用和其他警告
- 将现有待弃用转换为完全弃用
- 将现有警告转换为错误
- 需要在 What’s New 文档中添加“移植到 Python X.Y”条目才能引入的其他更改
功能版本系列的关键特征
- 一个功能版本系列内的安装不会与其他功能版本系列的安装冲突(即它们可以并行安装)
- 可以在一个功能版本系列内将安装更新到同一系列中的更高微版本,而无需重新安装或对以前安装的组件进行任何其他更改
基线功能版本的关键特征
- 在基线功能版本中,
sys.version_info.feature_complete == False
- 在基线功能版本中,
sys.version_info.micro == 0
- 基线功能版本可能包含对语言和解释器的高风险更改,例如语法修改、解释器和标准库内部的重大重构,或者可能具有侵入性的功能添加,这些添加可能会对其他现有功能产生意想不到的副作用
- 在基线功能版本中引入的功能是*唯一*允许依赖
sys.version_info
作为其功能可用性的唯一运行时指示器
围绕功能版本系列和基线功能版本的关键预期
- 大多数公共项目将仅针对版本系列中的*最新*微版本进行积极测试
- 许多(大多数?)公共项目只会在发布初始基线功能版本*之后*将其测试矩阵添加到新的版本系列中,这可能难以解决需要提供新标志或 API 来在默认行为更改后明确选择旧行为的问题
- 具有已知目标环境的私有项目将针对他们实际使用的任何微版本进行测试
- 大多数私有项目也只会在发布初始基线功能版本*之后*考虑迁移到新的版本系列,如果解决问题需要添加 API,这又会造成问题
本 PEP 中提案的关键动机是上面描述的公共和私有项目行为并不是*新的*期望:它们是对 CPython 版本系列如何被当今更广泛的社区处理的描述。因此,PEP 代表着试图调整我们的发布策略和流程,使其更好地匹配更广泛的社区已经处理它们的方式,而不是以这样一种方式改变我们的流程,即更广泛的社区需要适应我们,而不是相反。
增量功能发布
增量功能版本是本 PEP 提出的关键新流程添加。它们与现有的维护版本具有相同的严格运行时兼容性要求,但对 API 添加和增强有以下更宽松的策略
- 可以向任何标准库模块(包括内置模块)添加新的公共 API
- 根据以下功能检测要求,可以向现有 API(包括内置 API)添加新的可选参数
- 可以向稳定的 C ABI 添加新的公共 API(使用适当的版本保护)
- 可以向 CPython C API 添加新的公共 API
- 在发布经理批准的情况下,可以对现有 API 和语法结构进行向后兼容的可靠性改进
- 在发布经理批准的情况下,可以为现有 API 和语法结构合并性能改进
这种策略变化的目的是允许更及时地交付新(和现有!)语言功能的可用性改进,而不是要求用户承担等待下一个功能版本系列并升级的固有延迟和成本。
它还被设计为将添加功能到下一个基线功能版本的批准与是否将其提供给当前版本系列的下一个增量功能版本分开考虑,这可能允许志愿者贡献者完成第一个任务,而第二个活动可以由付费贡献者处理(例如,商业 Python 再发行者的客户可能要求其供应商回退功能,或者核心开发人员可能在合同基础上提供回退特定回退)。(以这种方式限制错误修复会存在潜在的道德问题,但这些问题不适用于新功能的回退)
增量功能版本的关键特征
- 在增量功能版本中,
sys.version_info.feature_complete == False
- 在增量功能版本中,
sys.version_info.micro != 0
- 在增量功能版本中进行的所有 API 添加必须支持不依赖于
sys.version_info
或运行时代码对象自省的有效运行时功能检测。在大多数情况下,对受影响模块进行简单的hasattr
检查将满足此目的,但当它不满足时,将需要在添加功能时实现替代方法。这方面的先例包括pickle.HIGHEST_PROTOCOL
属性,hashlib.algorithms_available
集合以及os
模块已经为平台相关功能检测提供的各种os.supports_*
集合 - 为了在混合版本环境中维护 pickle 兼容性,并为了在同一版本系列中跨多个 API 版本更容易进行兼容性测试,在增量功能版本中进行的所有 API 添加必须支持下一节中描述的新
sys.feature_limit
设置
围绕增量功能版本的关键预期
- “升级时不破坏现有安装”仍然是对所有微版本的关键要求,即使有更宽松的更改包含策略
- 更具侵入性的更改仍应推迟到下一个基线功能版本
- 开始依赖增量功能版本中添加的功能的公共 Python 项目应该适当地设置它们的
Python-Requires
元数据(项目在必要时已经这样做 - 例如,aiohttp
由于asyncio.get_event_loop()
在早期版本中的问题,因此专门需要 3.5.3 或更高版本)
某些标准库模块也可能对其在增量功能版本中允许的更改施加自己的限制(例如,只有基线功能版本应该在hashlib.algorithms_guaranteed
中添加新的哈希算法 - 增量功能版本只允许将算法添加到hashlib.algorithms_available
)
维护跨增量功能发布的互操作性
使用 Python 的pickle
模块在运行不同版本的 Python 的 Python 进程之间交换信息是一种常见做法。在版本系列之间,预计这种兼容性只能单向运行(即,排除弃用的 API,Python “X.Y+1” 进程应该能够读取 Python “X.Y” 进程生成的 pickle 档案,但反之则不成立,因为较新的档案可能引用较旧版本中不存在的属性和参数)。
然而,在一个版本系列内,预计它将在两个方向上都成立,因为“没有新功能”策略意味着几乎所有在 Python “X.Y.Z+1” 上创建的 pickle 档案都可以在 Python “X.Y.Z” 进程中读取。
同样,Python 库和应用程序通常只针对版本系列中的最新版本进行测试,这通常足以使代码在同一系列中的早期版本上运行。
允许在后来的“X.Y.Z” 版本中添加功能,而没有关闭它们的方法,将对这些常见做法构成问题,因为在 CPython 版本“X.Y.Z” 上测试时工作正常的库或应用程序如果使用在“X.Y.Z” 中新引入的功能,将在早期版本上失败,并且它创建的任何依赖于这些新接口的 pickle 档案也可能无法在旧版本上读取。
为了帮助解决这些问题,将添加一个新的sys.feature_limit
属性,它是一个结构化的序列,对应于sys.version_info
中的前 3 个字段(major
、minor
、micro
)。
将使用新的 CLI 选项(--feature-limit X.Y.Z
)和环境变量(PYTHONFEATURELIMIT=X.Y.Z
)来设置此属性。 PyCoreConfig
结构也将获得一个新字段
wchar_t *feature_limit;
如果未显式设置限制,则它将默认为sys.version_info
中的前 3 个字段。如果将限制设置为超出sys.version_info[:2]
的下界和sys.version_info[:3]
的上界的范围,它将被钳制到这些边界,如有必要,用零填充。
例如,给定当前版本“3.9.3”,名义限制将转换为运行时sys.feature_limit
值,如下所示
3 => (3, 9, 0)
3.8.1 => (3, 9, 0)
3.9 => (3, 9, 0)
3.9.2 => (3, 9, 2)
<unset> => (3, 9, 3)
3.9.3 => (3, 9, 3)
3.9.4 => (3, 9, 3)
4 => (3, 9, 3)
预计回退到增量功能版本的新的 API 将包含一个保护,如果功能限制过低,则会从模块中删除 API
def feature_api():
...
_version_feature_api_added = (3, 9, 1)
if _version_feature_api_added > sys.feature_limit:
del feature_api
同样,预计新的参数将包含一个保护,用于调整函数签名以匹配旧的签名
def feature_api(old_param1, old_param2, new_param=default):
"""Updated API docstring"""
...
_version_feature_api_changed = (3, 9, 1)
if _version_feature_api_changed > sys.feature_limit:
_new_feature_api = feature_api
def feature_api(old_param1, old_param2):
"""Legacy API docstring"""
return _new_feature_api(old_param1, old_param2)
这样构建保护措施可以最大程度地保持主开发分支和回退分支之间的代码结构相似,从而确保将来的错误修复仍然可以自动回退。
预计最终会添加便利函数和/或额外的自动化测试,以帮助确保这些回退的 API 得到适当的保护,但似乎可以等到有具体的例子才能驱动这些 API 和自动化测试的设计,而不是仅仅根据假设的例子进行设计。
功能完整发布和后续维护发布
对于给定的功能发布系列,功能完整的版本将在增量功能发布的正常策略下开发,但将具有一个区别特征
- 在功能完整的版本中,
sys.version_info.feature_complete == True
任何后续的维护和安全修复版本也将设置该标志,并且可以非正式地称为“功能完整的版本”。但是,为了发布系列定义的目的,功能完整的版本是第一个将该标志设置为“True”的版本。
针对临时 API 的拟议策略调整
为了帮助提高对临时 API 管理的一致性,本 PEP 建议对临时 API 施加定期的向后兼容性要求,这些要求遵循给定发布系列的功能完整版本。
管理临时 API 的其他方面将保持现状,因此,只要 API 保持在临时状态,定期向后兼容性要求将不适用于基线和增量功能发布中的该 API。
根据对 CPython 自 3.0.0 版本以来微版本中记录的 API 添加和更改的分析,预计该策略将为最终用户提供更大的清晰度(因为即使是临时 API 也会在功能完整的版本中稳定到该发布系列),而标准库维护人员几乎没有实际的缺点。
- 21 个 3.x.1 版本添加/更改说明
- 30 个 3.x.2 版本添加/更改说明
- 18 个 3.x.3 版本添加/更改说明
- 11 个 3.x.4 版本添加/更改说明
- 1 个 3.x.5 版本添加/更改说明
- 0 个 3.x.6+ 版本添加/更改说明
当需要进行基线发布后的更改时,大多数更改发生在前两个维护发布中,这些维护发布始终在基线发布后的 12 个月内完成。
(注意:这些计数并非仅针对临时 API - 它们涵盖了所有在基线发布后进行了语义更改的 API,这些更改被认为有必要在文档中说明。为了避免重复计数更改,这些数字不包括“新变化”部分中的任何更改标记。)
动机
本 PEP 中更改的动机本质上与 PEP 596 中更改的动机相同:当前功能发布之间 18-24 个月的间隔会导致许多不良后果,尤其是对于标准库(有关详细信息,请参阅 PEP 596)。
本 PEP 对 PEP 596 中的具体提议的关注点是,它将积极支持的 Python 分支数量增加了一倍,这增加了整个 Python 社区兼容性测试矩阵的复杂性,增加了在不使用稳定 ABI 时要上传到 PyPI 的二进制 Python 轮子的数量,并且通常有很高的可能性会给整个 Python 生态系统带来相对较高的额外成本。
本 PEP 中采用的观点是,存在一种替代方法,可以提供更快的功能发布的大多数优势,而不会实际产生相关成本:我们可以将当前的 X.Y.0“功能冻结”分成两部分,这样基线 X.Y.0 版本仅强制执行“运行时兼容性冻结”,而完整的标准库功能冻结则推迟到发布系列生命周期的后期。
注意事项和限制
本提议不适用于 Python 3.8 - 它仅针对 Python 3.9 及更高版本发布。
实际发布日期可能会根据发布团队的可用性和其他事件(例如 PyCon US 或年度核心开发冲刺)的时间安排,在发布管理器的酌情权范围内调整提前或延迟最多一个月。但是,本提议的目标之一是为贡献者和最终用户提供一致的年度节奏,因此调整理想情况下应该很少见。
本 PEP 不会规定发布系列内微版本的特定节奏 - 它只是指定了发布系列生命周期阶段(预 alpha、alpha、beta、功能发布、错误修复、安全修复)之间转换的大致时间线。每个阶段的微版本数量由该系列的发布经理根据他们和该系列的发布团队的其他成员准备承担相关工作的频率来确定。
但是,为了示例时间线的目的,PEP 假设季度微版本(Python 3.6 和 3.7 使用的节奏,将一些历史发布系列使用的半年一次节奏与为 Python 3.8 和 3.9 计划的每月一次节奏之间的差异分开了)。
设计讨论
为什么选择此提案而不是简单地更频繁地进行基线功能发布?
基线功能发布中涉及的文件系统布局更改和其他本质上不兼容的更改,为更广泛的 Python 社区的大部分人带来了额外的工作。
将这些布局更改与 Python 版本编号方案分离也是一项本身就需要进行向后不兼容更改的事情,以及调整社区对哪些版本可以安装在彼此之上,以及哪些版本可以在单个系统上并行安装的预期。
我们也没有直接的方法来向社区传达支持期限的变化,例如“仅支持 Python 版本 X.Y 直到 X.Y+1 发布,但支持 X.Z 直到 X.Z+2 发布”。
因此,本 PEP 的出发假设是,大多数 Python 用户根本不需要关心我们正在更改发布策略,而只有那些急切地等待标准库改进(以及其他向后兼容的解释器增强)的人,以及那些需要在复杂的部署环境中管理关键任务应用程序的人会受到影响。
对 Python 库开发的影响
许多 Python 库(开源和专有)目前采用仅针对每个功能发布系列中项目仍然支持的最新微版本进行测试的做法。
本 PEP 中的设计假设是,这种做法将在发布系列的功能发布阶段继续进行,预期是任何选择在功能完整之前采用新的发布系列的人员都将密切跟踪增量功能发布。
支持先前功能发布系列的库不太可能采用增量功能发布中添加的功能,如果它们确实采用这样的功能,那么任何相关的回退兼容性策略都应该以有效的方式在该发布系列中更早的版本上也生效。
对拟议的科学 Python 生态系统支持期的影响
根据 SciPy 2019 的讨论,目前正在起草一个 NEP [2],以定义科学 Python 生态系统中一个通用的约定,用于放弃对旧版 Python 版本的支持。
虽然该策略的具体表述仍在讨论中,但最初的提议非常简单:支持在过去 42 个月内发布的任何 Python 功能版本。
对于 18 个月的功能发布节奏,这意味着始终至少支持两个最新的功能版本,然后在大约 X.(Y+2).0 发布 6 个月后放弃对所有 X.Y.z 版本的支持。这意味着大约每隔一年就会有一个 6 个月的时期,在此期间将支持三个最新的功能版本。
对于 12 个月的发布节奏,它将相当于始终至少支持三个最新的功能版本,然后在大约 X.(Y+3).0 发布 6 个月后放弃对所有 X.Y.z 版本的支持。这意味着在每年的半年时间里,将支持四个最新的功能版本。
对于 24 个月的发布节奏,42 个月的支持周期将相当于始终至少支持最新的功能版本,然后在大约 X.(Y+1).0 发布 18 个月后放弃对所有 X.Y.z 功能版本的支持。这意味着大约每隔一年就会有一个 6 个月的时期,在此期间只支持一个功能版本(并且该时期与 X.(Y+2).0 基线功能版本的预发布测试周期重叠)。
对于本 PEP 中的提议,重要的是,该支持期限将遵守以下建议:库开发者应维护对先前发布系列的支持,直到最新发布系列达到功能完整状态:在基线功能发布后 18 个月放弃支持将大致相当于在功能完整发布后 6 个月放弃支持,而无需准确跟踪哪个版本将系列标记为功能完整。
对简单部署环境的影响
在本 PEP 的目的中,“简单”部署环境是指任何可以轻松确保所有目标环境同时(或至少在推出新的高级应用程序版本之前)更新到新的 Python 微版本的情况,并且没有对旧版 Python 版本能够可靠地读取使用较新 Python 版本生成的 pickle 流的要求,因此任何发生的预发布测试只需要针对单个 Python 微版本。
最简单的例子是个人使用的脚本,其中测试和目标环境是完全相同的环境。
类似的简单环境将是容器化的 Web 服务,其中在 CI 管道中使用与部署中相同的 Python 容器,以及任何捆绑自身 Python 运行时的应用程序,而不是依赖于目标系统上预先存在的 Python 部署。
对于这些用例,本 PEP 不会产生任何重大影响 - 只需要测试单个微版本,与该版本是否功能完整无关。
对复杂部署环境的影响
在本 PEP 的目的中,“复杂”部署环境是指不满足上述“简单部署”标准的用例:新的应用程序版本与同一个发布系列中的两个或多个不同的微版本结合起来作为部署过程的一部分,而不是总是每次只针对一个微版本。
如果本 PEP 中的提议能够如预期那样缩短功能交付延迟,那么可以预期,使用尚未达到功能完整状态的发布系列的开发者实际上会使用这些新功能。然后这意味着,与更早的维护发布相比,针对更新的增量功能发布的测试对于基线功能发布和更旧的增量功能发布的兼容性测试的有效性更低。
处理这种情况的一种选择是,在该系列达到“功能完整”状态之前,禁止使用新的 Python 版本。许多组织在采用新的功能发布系列时实际上已经采用了这样的策略,在原始发布后的几个月甚至几年内才会将其纳入运营环境。如果采用了该策略,那么这些组织可能仍然能够每隔一年采用一个新的 Python 版本 - 它只是基于功能完整的版本的可用性,而不是基于基线功能版本。
与完全禁止相比,一个不太严格的替代方案是使用提议的PYTHONFEATURELIMIT
设置,以启用逐步迁移到新的增量功能版本。
- 最初推出 Python X.Y.0,在 CI 和部署中设置
PYTHONFEATURELIMIT=X.Y.0
。 - 将 Python X.Y.1 推出到 CI,保留
PYTHONFEATURELIMIT=X.Y.0
设置。 - 根据 CI 成功结果,将 Python X.Y.1 部署到生产环境。
- 更新部署环境以设置
PYTHONFEATURELIMIT=X.Y.1
。 - 只有在所有部署环境都更新后,才在 CI 中设置
PYTHONFEATURELIMIT=X.Y.1
。 - 对每个新版本重复此过程,直到且包括该发行系列的功能完整版本。
- 一旦该系列的功能完整,为了保持一致性,要么继续使用相同的过程,要么停止更新
PYTHONFEATURELIMIT
,并将其保留在功能完整的版本号。
功能添加期的持续时间
本 PEP 建议在初始基线功能版本发布后 12 个月内限制添加功能。
这样做的主要动机是与 Ubuntu LTS 时间同步,以便 Python 3.9.x 系列的功能完整版本在 2021 年 10 月发布,为包含在 Ubuntu 22.04 版本中做好准备。(其他 LTS Linux 发行版,如 RHEL、SLES 和 Debian,没有固定的发布节奏,因此它们可以更容易地调整其 LTS 时间,以与输入的稳定版本保持一致。Canonical 有意地没有在自己的发布周期中给自己这种灵活性)。
12 个月的功能添加周期来自将 2019 年 10 月发布的 Python 3.8.0 和 2021 年 10 月最终的 Python 3.9.x 增量功能版本之间的时间平均分配到预发布开发和后续增量功能版本之间。
这是一个本 PEP 可以采用PEP 596中提议的部分内容的领域,而是将该拆分改为大约 9 个月的预发布开发和大约 15 个月的增量功能版本。
- 2019-11: 3.9.0a1
- …由发布经理确定的其他 alpha 版本
- 2020 年 3 月:3.9.0b1
- 2020 年 4 月:3.9.0b2
- 2020 年 5 月:3.9.0b3(锁定 ABI 兼容性的最终 beta 版本)
- 2020 年 6 月:3.9.0rc1
- …由发布经理确定的其他候选版本
- 2020 年 7 月:3.9.0(BFR)
- 2020 年 10 月:3.9.1(IFR)
- 2021 年 1 月:3.9.2(IFR)
- 2021 年 4 月:3.9.3(IFR)
- 2021 年 7 月:3.9.4(IFR)
- 2021-10: 3.9.5
- 2022-01: 3.9.6
- 2022-04: 3.9.7
- 2022-07: 3.9.8
- 2022 年 10 月:3.9.9(最终的定期维护版本)
- …根据需要发布其他仅安全修复版本
- 2025-10: 3.9.x 分支关闭
这种方法意味着始终会有两个或三个活跃的分支,只是与“预发布”、 “预 beta”和“预发布”阶段相比,分支处于“功能发布”阶段的时间比例会更高。
- 2019-04 -> 2019-10: 3.9.0 预发布版,3.8.0 预发布版,3.7.x 维护版
- 2019 年 10 月 -> 2020 年 3 月:3.9.0 预 beta,3.8.x 维护
- 2020 年 3 月 -> 2020 年 7 月:3.10.0 预发布,3.9.0 预发布,3.8.x 维护
- 2020 年 7 月 -> 2021 年 10 月:3.10.0 预发布,3.9.x 功能发布,3.8.x 维护
- 2021 年 10 月 -> 2022 年 3 月:3.10.0 预 beta,3.9.x 维护
- 2022 年 3 月 -> 2022 年 7 月:3.11.0 预发布,3.10.0 预发布,3.9.x 维护
- 2022 年 7 月 -> 2023 年 10 月:3.11.0 预发布,3.10.x 功能发布,3.9.x 维护
- 2023 年 10 月 -> 2024 年 3 月:3.11.0 预 beta,3.10.x 维护
- 2024 年 3 月 -> 2024 年 7 月:3.12.0 预发布,3.11.0 预发布,3.10.x 维护
- …等等
未发布的预发布版本的持续时间
在本 PEP 的基线提议中,提议的时间表仍然包括我们从主 git 分支发布 18 个月而不发布版本的时期(例如,3.9.0b1 将在 2020 年 5 月分支出来,3.10.0a1 直到 2021 年 11 月才会发布)。它们只是允许在这些月份中,将各种各样的更改回溯到最新的维护分支。
将 beta 分支点移到发行系列生命周期中更早时间的提议变体将使这种没有直接发布的时期延长到 21 个月 - 唯一直接从主分支发布的时期是在先前发行系列的最后一个增量功能版本和几个月后的 beta 分支点之间的相对较短的时间窗口内。
虽然将年度节奏在“大型基础增强”和“针对性的低风险 API 可用性改进”之间交替是本提议的故意特性,但如果在先前发行系列分支出来后不久就进行了更改,等待那么长时间来获得反馈似乎仍然很奇怪。
处理此问题的另一种方法是在功能添加期间开始发布下一个基线功能版本的 alpha 版本(类似于PEP 596提议在 Python 3.8.0 发布候选版本期间开始发布 Python 3.9.0 alpha 版本的方式)。
但是,与其在策略层面为其设置特定时间表,不如将此决定留给各个版本管理员,由他们根据正在针对其管理的版本提出的具体更改来决定。
为什么不直接切换到完整的语义版本控制?
如果这是一个新语言的版本设计文档,它 *将* 使用语义版本控制:上面描述的针对基线功能版本的策略将应用于 X.0.0 版本,针对增量功能版本的策略将应用于 X.Y.0 版本,针对维护版本的策略将应用于 X.Y.Z 版本。
Python 特定的问题是,与并行安装支持和 ABI 兼容性定义相关的所有策略和属性当前都与版本号的 *前两个* 字段相关联,而且在过去三十年中一直如此。
因此,将引入增量功能版本的策略问题与使版本编号方案更好地匹配不同版本类型语义的技术问题分开是合理的。
如果本 PEP 中的提议被 Python 3.9 的指导委员会接受,那么解决该技术问题的最佳时机是在随后的 2022 年 10 月基线功能版本发布时,因为选择“Python 4.0”(错误地检查主版本是否正好为 3 而不是 3 或更大)或“Python 3.10”(代码错误地假设次版本将始终包含一个十进制数字)[1]本身就存在着固有的兼容性风险。
虽然本 PEP 的文本假设 2022 年发布的版本将是 3.10(因为 PEP 作者个人认为这是更合理且最有可能的选择),但该决定两方面都有复杂的利弊,而本 PEP 确实为选择“Python 4.0”选项增加了一个潜在的优势(前提是我们还需要修改受影响的安装布局和兼容性标记,以便仅考虑主版本号,而不是主版本号和次版本号)。
如果对版本编号进行此类更改并被接受,那么上面给出的示例 3.10.x 时间表将变为以下 4.x 系列时间表。
- 2021 年 11 月:4.0.0a1
- …由发布经理确定的其他 alpha 版本
- 2022 年 5 月:4.0.0b1
- …由发布经理确定的其他 beta 版本
- 2022 年 8 月:4.0.0bX(锁定 ABI 兼容性的最终 beta 版本)
- 2022 年 9 月:4.0.0rc1
- …由发布经理确定的其他候选版本
- 2022 年 10 月:4.0.0(BFR)
- 2023 年 1 月:4.1.0(IFR)
- 2023 年 4 月:4.2.0(IFR)
- 2023 年 7 月:4.3.0(IFR)
- 2023 年 10 月:4.4.0(IFR)
- 2024-01: 4.4.1
- 2024-04: 4.4.2
- 2024-07: 4.4.3
- 2024 年 10 月:4.4.4(最终的定期维护版本)
- …根据需要发布其他仅安全修复版本
- 2027 年 10 月:4.x 分支关闭
5 年的日程安排预测将如下所示。
- 2019-04 -> 2019-10: 3.9.0 预发布版,3.8.0 预发布版,3.7.x 维护版
- 2019-10 -> 2020-05: 3.9.0 预发布版,3.8.x 维护版
- 2020 年 5 月 -> 2020 年 10 月:4.0.0 预发布,3.9.0 预发布,3.8.x 维护
- 2020 年 10 月 -> 2021 年 10 月:4.0.0 预发布,3.9.x 功能发布,3.8.x 维护
- 2021 年 10 月 -> 2022 年 5 月:4.0.0 预 beta,3.9.x 维护
- 2022 年 5 月 -> 2022 年 10 月:5.0.0 预发布,4.0.0 预发布,3.9.x 维护
- 2022 年 10 月 -> 2023 年 10 月:5.0.0 预发布,4.x.0 功能发布,3.9.x 维护
- 2023 年 10 月 -> 2024 年 5 月:5.0.0 预 beta,4.x.y 维护
- 2024 年 5 月 -> 2024 年 10 月:6.0.0 预发布,5.0.0 预发布,4.x.y 维护
- …等等
参考资料
版权
本文件已进入公有领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0598.rst