首页
/ Requests 版本发布规则详解:Major、Minor、Hotfix 三级发布策略、v2.6.2 历史背景与仓库实现现状

Requests 版本发布规则详解:Major、Minor、Hotfix 三级发布策略、v2.6.2 历史背景与仓库实现现状

2026-09-05 21:12:56作者:温艾琴Wonderful

本篇技术文章以 Requests 仓库的发布流程文档(docs/community/release-process.rst)为核心,完整解读 Requests 核心团队执行了十余年的三级发布策略:Major(主版本)、Minor(次版本)、Hotfix(热修复)各自的适用范围、版本号演进规则与兼容性承诺。文章同时结合仓库源码与发布历史,还原这套规则诞生的真实历史背景——2.5/2.6 时期内置(vendored)依赖升级引发的用户困境,并对照 pyproject.tomlHISTORY.md 与当前源码,说明该规则在版本声明、构建打包和依赖声明层面的具体落点,帮助你在做依赖升级、故障定位和兼容性评估时准确预期"哪个版本级别会包含什么内容"。

规则总览与版本命名约定

这套发布规则自 v2.6.2 之后的下一个版本开始生效(文档中标注 .. versionadded:: v2.6.2),用于约束并描述 Requests 核心团队如何产出一个新版本。它采用的正是语义化版本(Semantic Versioning)的三段式命名,但文档对每一段的语义做了明确的项目化约定:

发布级别 版本号变化 允许包含的内容 兼容性承诺
Major(主版本) vX.Y.Zv(X+1).0.0 破坏性变更 + 一般性 bug 修复 不保证向后兼容
Minor(次版本) vX.Y.ZvX.(Y+1).0 新特性 + bug 修复,不含破坏性变更 与同主版本号的所有版本相互兼容
Hotfix(热修复) vX.Y.ZvX.Y.(Z+1) 修复上一版本遗漏的 bug 向后兼容

文档以 v10.2.7 为基线给出了三个示例:下一个主版本号为 v11.0.0,下一个次版本号为 v10.3.0,下一个热修复版本号为 v10.2.8。结合当前仓库即可验证这一约定的真实轨迹——src/requests/version.py 中声明的版本为 2.34.2,而 HISTORY.md 中 2026-05-11 的 2.34.0 携带新特性公告,紧接着的 2.34.12.34.2 则全是纯 bug 修复,正好构成一次"次版本 + 两次热修复"的标准组合。

各发布级别的核心约束

Major(主版本)发布包含破坏性变更(breaking changes),即破坏与之前版本向后兼容的改动。文档给出的判断标准非常具体:如果要把 Response 对象的 text 属性改为方法,这种调用方式层面的改动只能发生在主版本中。同时文档强调三点工程立场:

  • 主版本可以附带一般性 bug 修复(miscellaneous bug fixes);
  • 核心团队承诺提供良好的用户体验,因此尽可能保持向后兼容;
  • 主版本发布将保持低频,且必须在获得充分理由(strong justifications)之后才会被考虑。

Minor(次版本)发布不包含破坏性变更,可包含一般性 bug 修复与新特性。其兼容性承诺是:与具有相同主版本号的所有发布相互兼容——换言之,所有以 v10. 开头的版本之间应彼此兼容。对下游用户而言,这意味着在同一个主版本号内做跨次版本升级(例如 2.33.x → 2.34.x)时,API 行为预期是稳定的。

Hotfix(热修复)发布只包含那些在上一版本发布时被遗漏的 bug 修复。Hotfix 是变更范围最窄的发布级别:它不引入特性、不升级依赖(详见下文"Hotfix 为何禁止内置依赖升级"一节)。从 HISTORY.md 中可以看到热修复的典型形态,例如 2.34.1 修复了"自定义 __getattr__ 实现的 body 未被正确识别为 Iterable"等问题,2.34.2 则把 headers 输入类型从 MutableMapping 回调为 Mapping 以解决不变性带来的类型推断问题——后者本质上是修复上一次热修复引入的副作用,是 Hotfix 语义的教科书级案例。

历史背景:规则为何在 v2.6.2 之后诞生

发布流程文档的 "Reasoning" 一节交代了这套规则的由来:在 2.5 与 2.6 发布系列中,Requests 核心团队升级了内置(vendored)依赖,给用户和核心团队双方都带来了大量麻烦(headaches)。为减少这种痛苦,团队决定形成一套具体的、成文的流程,以便各方预期得到恰当设定。

这段历史可以直接在 HISTORY.md 中找到实证:

  • 2.7.0 (2015-05-03) 条目明确写道:"This is the first release that follows our new release process.",并说明该版本"Updated urllib3 to 1.10.4, resolving several bugs involving chunked transfer encoding and response framing."(升级 urllib3 以修复若干分块传输编码与响应帧问题)。这正是新流程下第一个正式发布,其中就包含一次内置 urllib3 的升级。
  • 更早的 2.6.02.5.2 等条目则记录了 vendored urllib3 的升级、回滚与证书包更新,例如 2.5.3 的 "Revert changes to our vendored certificate bundle"(回滚对内置证书包的改动)。这些条目从侧面印证了文档所说的"内置依赖升级曾造成大量连锁问题"。

Hotfix 为何禁止内置依赖升级:规则全文与工程原理

发布流程文档中有一条最容易被忽视、但对生产环境升级决策影响最大的硬性规定(引自 docs/community/release-process.rst):

在 v2.6.2 之后,Hotfix 版本不会包含对 vendored dependencies(内置依赖)的升级。

这条规则将"依赖升级"这一高不确定性操作明确排除在最小变更范围的发布级别之外,其工程原理在于:

  1. 变更范围可预期。Hotfix 的唯一承诺是"只修上一版本遗漏的 bug",用户拉取一个补丁号(vX.Y.ZvX.Y.(Z+1))时,可以确信其中不含任何依赖库行为变化,升级风险被压缩到最低。
  2. 依赖升级归属更高版本级别。内置依赖的行为变化(新特性、新 bug、新的安全修复)被归入次版本或主版本,用户在做这类升级时本来就需要重新审视行为差异,预期因此得到恰当设定——这正是文档 Reasoning 一节所追求的目标。
  3. 历史教训的直接对策。2.5/2.6 时期的问题根源正在于:补丁号级别的发布中夹带了 vendored urllib3 的升级,用户在"只是打个补丁"的心理预期下遭遇了底层库行为变化。规则成文后,这类情形从流程上被禁止。

规则在当前仓库中的落点

结合仓库现状,这条历史规则可以对照今天的依赖管理模式来理解——Requests 早已"Unvendor ALL the things"(该口号记载于 HISTORY.md 的 2.16.0 条目),内置依赖全部外部化。当前 pyproject.toml 声明了四组外部依赖及其版本区间:

dependencies = [
    "charset_normalizer>=2,<4",
    "idna>=2.5,<4",
    "urllib3>=1.26,<3",
    "certifi>=2023.5.7"
]

可以看到版本区间用 <4<3 这样的上界锁住了跨主版本的自动漂移——这与"依赖行为变化只应发生在有预期的版本级别中"的思想一脉相承。

与此同时,src/requests/packages.py 仍然保留了对 urllib3idna(及 chardet)的兼容别名机制。该模块的注释直言这是"为向后兼容而存在"的代码:它通过遍历 sys.modulesrequests.packages.urllib3.* 等旧导入路径映射到真实安装的模块上,使得历史上依赖 vendored 导入位置的第三方代码在依赖外部化之后依然可以工作。从源码结构看,这正是当年 vendored 时代(也就是 Hotfix 规则诞生背景中)遗留下来的兼容性遗产,与发布规则的历史背景形成了完整的呼应链条。

发布流程在仓库构建体系中的落点

理解这套发布规则,还需要知道"发布"在当前仓库中具体如何产出,以下均可在仓库中直接查证:

版本号是构建期的唯一事实来源。 pyproject.tomlversion 声明为 dynamic,并配合 pyproject.toml 中的动态版本配置 version = {attr = "requests.__version__.__version__"},即打包时的版本号直接从 src/requests/version.py__version__ = "2.34.2" 读取。该文件同时维护 __build__ 构建号(0x023402,与 2.34.2 对应)及项目元信息(__title____author____license__ 等)。这意味着每次发布的核心动作之一,就是更新这个单点版本号,再按规则决定它是 X.0.0X.Y.0 还是 X.Y.Z+1

PEP 517 标准构建保证发布可复现。 自 2.33.0 起,项目"迁移到基于 setuptools 的 PEP 517 构建系统"(见 HISTORY.md 中 2.33.0 的 Improvements 条目)。当前 pyproject.toml 声明 requires = ["setuptools>=61.0"]build-backend = "setuptools.build_meta"pyproject.toml 指定从 src 目录发现包,tool.setuptoolsLICENSENOTICE 一并打入发行包。setup.py 则退化为一个极薄的兼容入口,仅在 sys.version_info < (3, 10) 时打印错误并退出,否则直接调用 setup()——这与 pyproject.tomlrequires-python = ">=3.10" 的约束相互印证:发布产物在构建阶段就会拒绝低于 Python 3.10 的环境。

发布节奏与内容可从发布历史直接校验。 HISTORY.md 的顶部保留了 dev 段落作为下一个待发布版本的变更收集区,其下按版本倒序排列全部发布记录。以最近的三个版本为例,规则与记录的对应关系非常清晰:

  • 2.34.0 (2026-05-11):包含 Announcements(内联类型替代 typeshed)、Improvements(新增 Python 3.15/3.14t 支持)与 Bugfixes,属典型的 Minor 内容;
  • 2.34.1 (2026-05-13):仅含 Bugfixes(json/headers 类型放宽、Response.reason 类型收窄等),为纯热修复;
  • 2.34.2 (2026-05-14):单条类型回调修复,同样是热修复,并展示了热修复如何用于纠正前一次热修复的副作用。

此外,tests/test_packages.pypackages.py 的兼容别名机制有专门测试覆盖,说明这条"历史兼容遗产"在每次发布前的测试流程中都会被回归验证,进一步保证了发布过程的确定性。

小结

Requests 的发布流程文档篇幅不长,却定义了该项目十余年版本演进的全部骨架:Major 承载破坏性变更且低频发布、Minor 在向后兼容前提下引入特性与修复、Hotfix 只做遗漏 bug 的补丁且自 v2.6.2 后严禁包含内置依赖升级。这套规则诞生于 2.5/2.6 时期 vendored 依赖升级引发的真实痛点,并在 2.7.0 成为首个执行的版本。今天对照仓库可见,规则已内化为具体的工程设施:动态版本号来自 src/requests/version.py,依赖上界锁在 pyproject.toml,构建由 PEP 517 + setuptools 标准化,而 HISTORY.md 中逐版本的变更分类(Announcements / Improvements / Bugfixes / Security / Deprecations)则为每一个发布级别提供了可追溯的实证记录。掌握了这套规则,你就可以在拉取任何版本前,先根据版本号变化判断其变更边界,从而做出可控的升级决策。

登录后查看全文
热门项目推荐
相关项目推荐