ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

pytest 变更日志生成机制:changelog/_template.rst 模板与 towncrier 新闻片段工作流解析

pytest 变更日志生成机制:changelog/_template.rst 模板与 towncrier 新闻片段工作流解析 pytest 变更日志生成机制changelog/_template.rst 模板与 towncrier 新闻片段工作流解析【免费下载链接】pytestThe pytest framework makes it easy to write small tests, yet scales to support complex functional testing项目地址: https://gitcode.com/GitHub_Trending/py/pytest本文以 pytest 仓库中负责生成CHANGELOG的核心模板 changelog/_template.rst 为主线系统讲解 pytest 如何通过 towncrier 将分散的「新闻片段newsfragment」聚合为一份面向用户的发布说明。读完本文你将理解模板的每一段 Jinja2 逻辑、pyproject.toml 中 10 类变更类型的配置方式、新闻片段的命名与写作规范以及该模板如何与 Sphinx 文档站点含草稿预览完成集成从而能够在 pytest 生态中熟练编写与审阅变更记录。一、模板在整个发布流程中的位置changelog/_template.rst并非一份普通的 RST 文档而是一份Jinja2 模板——它不直接展示给用户阅读而是被 towncrier 调用用于把changelog/目录下数百个零散的变更记录文件渲染成最终发布的CHANGELOG。整条流水线可以概括为changelog/*.type.rst新闻片段如 14743.feature.rst │ towncrier build配置读取自 pyproject.toml 的 [tool.towncrier] ▼ changelog/_template.rstJinja2 模板负责结构编排与 RST 渲染 ▼ doc/en/changelog.rst最终产物随文档站点发布这条链路的枢纽配置位于 pyproject.toml[tool.towncrier] package pytest package_dir src filename doc/en/changelog.rst directory changelog/ title_format pytest {version} ({project_date}) template changelog/_template.rst其中各字段的作用directory changelog/新闻片段的存放目录即仓库中的 changelog/filename doc/en/changelog.rst模板渲染结果的落盘位置即 doc/en/changelog.rsttemplate changelog/_template.rst显式指定本文的主角——渲染模板title_format pytest {version} ({project_date})生成版本标题例如最终成品中可以看到pytest 9.1.1 (2026-06-19)这种标题行见 doc/en/changelog.rst。doc/en/changelog.rst 开头也明确写着该文件由 towncrier 托管贡献者不应手动编辑只能通过新增新闻片段的方式来添加变更记录而第 32 行的.. towncrier release notes start注释则标明了渲染内容插入的位置。二、模板结构逐段拆解_template.rst全文约 40 行虽然精炼却完整承载了版本分组、类别编排、条目排序、空版本兜底等全部逻辑。下面逐段解读。2.1 外层遍历 section 与 RST 标题层级模板最外层的骨架{% for section in sections %} {% set underline - %} {% if section %} {{section}} {{ underline * section|length }}{% set underline ~ %} {% endif %}towncrier 会把变更数据组织成sections字典传给模板每个 key 是一个版本区段名pytest 当前配置未声明自定义 section因此区段名为空字符串。这段逻辑的含义是每轮循环先把underline初始化为-若区段名非空则输出区段名并用等长数量的-作为 RST 标题下划线RST 要求下划线长度与标题一致紧接着把underline改为~。这里的underline变量会在同一轮循环的后续代码中被继续使用见 2.4 的类别标题从而实现RST 标题层级的区分带名称的 section 用-作一级标题其下的类别用~作二级标题。由于 pytest 当前配置下区段名为空字符串{% if section %}不成立因此实际产物里类别标题使用-下划线这与 doc/en/changelog.rst 中Improved Documentation下方使用-的效果一致。2.2 空版本兜底No significant changes{% if sections[section] %} ... {% else %} No significant changes. {% endif %}如果某个版本区段下没有任何类别条目模板不会输出空标题而是渲染出一句No significant changes.。这是模板内置的兜底文案保证没有变更的版本也能生成结构完整的说明。2.3 类别循环只渲染存在条目的类别{% for category, val in definitions.items() if category in sections[section] %}definitions来自pyproject.toml中[[tool.towncrier.type]]的声明。if category in sections[section]是一个过滤条件——只有在该区段中实际存在条目的类别才会被渲染。这意味着没有任何条目的类别不会出现在产物中类别的渲染顺序严格遵循definitions的声明顺序先breaking最后misc而非随机顺序。2.4 类别标题{{ definitions[category][name] }} {{ underline * definitions[category][name]|length }}每个类别输出两行一行是pyproject.toml中为该类别配置的显示名称例如Bug fixes、New features一行是与其长度相同的下划线。下划线字符正是 2.1 节中维护的underline变量从而保证整份文档的标题层级自洽。2.5 条目渲染showcontent 分支这是模板的核心输出部分根据类别是否配置showcontent分为两条分支。分支一showcontent true渲染条目文本与 issue 链接{% if definitions[category][showcontent] %} {% for text, values in sections[section][category]|dictsort(byvalue) %} {% set issue_joiner joiner(, ) %} - {% for value in values|sort %}{{ issue_joiner() }}{{ value }} https://github.com/pytest-dev/pytest/issues/{{ value[1:] }}_{% endfor %}: {{ text }} {% endfor %}这段逻辑值得逐项拆解dictsort(byvalue)对同一类别下的条目按内容文本排序Jinja2 的dictsort默认按键排序此处显式指定按值排序values是与同一段文本相关联的 issue 编号列表values|sort对编号做数值排序issue_joiner joiner(, )Jinja2 的joiner工具在首次调用时返回空字符串之后返回,用于把同一条文本关联的多个 issue 链接用逗号拼接{{ value }}形如#14743而{{ value[1:] }}则去掉#前缀得到纯编号用于拼进 issue 链接RST 引用语法文本 URL_生成一个指向对应 issue 的超链接最终输出形如- #14743 issue链接_: 变更内容的列表项。分支二showcontent false仅列出 issue 编号不含文本{% else %} - {{ sections[section][category][]|sort|join(, ) }} {% endif %}当类别未开启showcontent时towncrier 把内容存放在 key 为空字符串的槽位里模板直接将其中的所有编号排序后用,连接输出形如- #123, #456的一行不附带任何描述文字。2.6 类别级空判断{% if sections[section][category]|length 0 %} No significant changes. {% else %} {% endif %}这是模板中的第二处兜底若某类别存在但条目数为零同样渲染No significant changes.。在当前 pytest 配置中由于 10 个类别全部声明了showcontent true2.5 节的分支二与这里的空判断属于模板自带的通用防御逻辑在 pytest 的常规发布中较少触发。三、模板的输入一新闻片段newsfragment规范模板渲染的原料是 changelog/ 目录下的新闻片段文件。目录入口文档 changelog/README.rst 给出了明确的规范命名规则每个文件命名为ISSUE.TYPE.rst其中ISSUE是 issue或 PR编号TYPE是变更类别。例如123.feature.rst456.bugfix.rst写作要求changelog/README.rst使用完整的句子采用过去时或现在时并正确使用标点内容应面向pytest 用户而不是只对开发者有意义的内部实现细节推荐的示例写法如Improved verbose diff output with sequences.、Terminal summary statistics now use multiple colors.towncrier 会完整保留多段落和各类 RST 格式代码块、列表等但对除feature之外的类别通常保持单段落更简洁。仓库中现存的片段可以直观印证这套规范例如changelog/14716.breaking.rst-c选项对不存在路径现在会报 usage errorchangelog/14743.feature.rstdiff 输出中新增/-符号图例changelog/14724.improvement.rst--no-summary不再跳过pytest_terminal_summary钩子changelog/14834.doc.rst文档新增第三方插件pytest-skip-slowchangelog/14758.misc.rst内部 node id 改为结构化NodeId数据类型。此外同一 issue 可以有多个片段例如 changelog/10745.improvement.1.rst 与 changelog/10745.improvement.2.rst它们最终会被合并到同一条变更记录中。四、模板的输入二pyproject.toml 中的类型定义模板遍历的definitions完全由 pyproject.toml 中的[[tool.towncrier.type]]列表驱动。pytest 共声明了 10 类变更每类的directory即新闻片段文件名的类型后缀name是渲染到变更日志中的章节标题directoryname渲染标题含义breakingRemovals and backward incompatible breaking changes以破坏性方式移除公共功能deprecationDeprecations (removal in next major release)声明未来的 API 移除与行为变更featureNew features面向用户的新功能、新命令行选项improvementImprovements in existing functionality既有功能的改进bugfixBug fixes缺陷修复vendorVendored libraries内置依赖的更新docImproved documentation文档结构与构建的改进packagingPackaging updates and notes for downstreams面向下游的打包与工具链说明contribContributor-facing changes影响贡献者体验的变更测试、文档构建、开发环境miscMiscellaneous internal changes难以归入上述类别的内部变更配置中每个类别都显式设置了showcontent true正如 2.5 节所述这意味着所有类别在渲染时都会输出完整的条目文本与 issue 链接。配置文件中的注释还说明之所以逐个显式声明类型是因为 towncrier 本身不允许只覆盖misc的showcontent值为了清晰与灵活性pytest 选择把全部类型一次性声明出来。五、模板的输出真实渲染效果将上述模板与配置组合后一段14743.feature.rst最终会在 doc/en/changelog.rst 中呈现为类似下面的格式RST 引用语法URL 由模板中的固定前缀与 issue 编号拼接而成New features ------------ - #14743 https://github.com/pytest-dev/pytest/issues/14743_: Full diff: now contains a legend explaining the - symbols in diff output.仓库历史中真实的渲染示例可以参见 doc/en/changelog.rst其中#12493那条记录即描述了草稿预览集成重构为sphinxcontrib-towncrier扩展的经过其行内格式与上述模板输出完全吻合- #12493 issue 链接_: The change log draft preview integration has been refactored to use a third party extension sphinxcontrib-towncrier.由此可以看到模板渲染的完整链路issue 编号 文本→ RST 引用链接 → 带章节标题的列表项 → 按版本聚合的完整变更日志。六、与文档站点的集成草稿预览_template.rst的产出并不会等到正式发版才可见。pytest 通过 Sphinx 扩展sphinxcontrib-towncrier在文档构建期间提供变更日志草稿预览。在 doc/en/changelog.rst 中可以看到To be included in v\ |release| (if present) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. towncrier-draft-entries:: |release| [UNRELEASED DRAFT]towncrier-draft-entries指令会在构建文档时把尚未发布的新闻片段实时渲染并嵌入到「未发布」章节中。相关的扩展配置位于 doc/en/conf.pytowncrier_draft_autoversion_mode draft # or: sphinx-version, sphinx-release towncrier_draft_include_empty True towncrier_draft_working_directory PROJECT_ROOT_DIR towncrier_draft_config_path pyproject.toml # relative to cwd这些选项分别控制草稿版本号的推断方式此处为draft、是否包含空区段、工作目录以及 towncrier 配置文件的路径。依赖方面doc/en/requirements.txt 中声明了sphinxcontrib-towncrier与sphinx-issues后者提供:issue:、:pr:等 GitHub 相关角色。对贡献者而言最直接的验证方式是运行tox -e docs构建文档然后在doc/en/_build/html/changelog.html中预览自己的变更记录在最终发布说明里的样子见 changelog/README.rst。七、贡献者视角如何添加一条变更记录结合 CONTRIBUTING.rst 的提交流程指引贡献者需要变更日志时的完整步骤为在changelog/目录下新建文件命名为issueid.type.rst其中type取自十类之一feature、improvement、bugfix、doc、deprecation、breaking、vendor、packaging、contrib、misc如果改动不影响 pytest 的文档化行为可以跳过此步骤若改动修复了某个 issue文件名直接使用该 issue 编号若当时没有对应 issue可在 PR 提交后改用 PR 编号不确定应选择哪种类别时直接在 PR 中询问维护者即可。从模板与配置的关系来看贡献者每新增一个文件就相当于为sections数据源注入一条记录towncrier 在发版时会自动完成排序、链接化与章节归并最终呈现为 doc/en/changelog.rst 中一份结构统一、层级分明的发布说明。小结changelog/_template.rst以约 40 行 Jinja2 代码完整定义了 pytest 变更日志的渲染规则section 遍历与 RST 标题层级、类别过滤与排序、showcontent双分支条目渲染、issue 链接拼接以及两处No significant changes.兜底。它的一端连接 pyproject.toml 中 10 类变更类型的声明另一端通过 doc/en/changelog.rst 与 Sphinx 文档站点相连并借助sphinxcontrib-towncrier提供实时草稿预览。理解这份模板就等于掌握了 pytest 从「一行变更记录」到「一份可读性极高的发布说明」的全部生成机制。【免费下载链接】pytestThe pytest framework makes it easy to write small tests, yet scales to support complex functional testing项目地址: https://gitcode.com/GitHub_Trending/py/pytest创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表