ARTICLE DETAIL

资讯详情

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

Renovate 的 Poetry 版本解析模块:PEP 440 与 SemVer 混合版本的转换与比较原理

Renovate 的 Poetry 版本解析模块:PEP 440 与 SemVer 混合版本的转换与比较原理 Renovate 的 Poetry 版本解析模块PEP 440 与 SemVer 混合版本的转换与比较原理【免费下载链接】renovateHome of the Renovate CLI: Cross-platform Dependency Automation by Mend.io项目地址: https://gitcode.com/GitHub_Trending/re/renovate本篇技术文章围绕 Renovate 仓库中的 Poetry 版本解析模块lib/modules/versioning/poetry/展开系统讲解其设计动机PEP 440 与 SemVer 的混用问题、版本/范围双向转换的实现原理、完整 API 的行为细节与边界限制并结合源码与测试用例说明getNewValue等关键函数如何生成pyproject.toml中依赖约束的新值。读完后你将理解 Renovate 如何在“按 Poetry 规范写入”与“按 npm/SemVer 规则计算”之间架起桥梁并能准确判断该模块对预发布版本、通配符约束、!排除等写法的支持边界。背景为什么 Poetry 需要独立的版本解析模块Poetry 的依赖约束写在pyproject.toml的[tool.poetry.dependencies]中与 Python 包索引PyPI上实际发布的版本号分属两套不完全一致的体系Poetry 约束在major.minor.patch层面兼容 SemVer 风格的语义如^1.2、~1.2.3、1.2,2.0但对预发布pre、后置发布post、开发版dev的表达采用 PEP 440 的形式PEP 440Python 官方定义的版本号规范如1.0a1、1.0rc1、1.0.post1、1.0.dev1与 SemVer 的-alpha.1、-rc.1等写法不同。模块自带的说明文档 lib/modules/versioning/poetry/readme.md 对此总结为“Poetry versioning is a little like a mix of PEP440 and SemVer”Poetry 的版本规则近似于 PEP 440 与 SemVer 的混合体并指出当前实现基于 npm 版本解析通过“按 Poetry 的模式解析 → 交给 npm 实现计算 → 反向还原归一化”来让 Renovate 能把pyproject.toml中 SemVer 风格的版本与 PyPI 上的 PEP 440 表示进行有意义的比较。“这些表示在 major.minor.patch 正式版本上等价但在 pre-、post- 和 dev 版本上并不相同。”Renovate 同时提供 pep440 版本解析模块lib/modules/versioning/pep440/用于纯 PEP 440 语境的比较而 Poetry 模块的定位是服务于 Poetry 生态下pyproject.toml的解析与约束改写。整体架构以 npm 版本解析为计算引擎从 lib/modules/versioning/poetry/index.ts 的源码结构看模块声明了基本属性export const id poetry; export const displayName Poetry; export const supportsRanges true; export const supportedRangeStrategies: RangeStrategy[] [ bump, widen, replace, ];即模块标识为poetryRenovate 配置中versioning: poetry即指向它支持范围range语义且支持bump、widen、replace三种rangeStrategy。核心设计是“翻译—计算—反翻译”三段式翻译函数全部集中在 lib/modules/versioning/poetry/transform.ts函数方向用途poetry2semverPoetry 版本 → SemVer 字符串把单个版本号如1.9.01b01转成标准 SemVer如1.9.1-beta.1供 npm 实现比较poetry2npmPoetry 范围 → npm 范围把1.0,2.0这类逗号分隔的约束翻译成 npm 可解析的空格分隔形式semver2poetrySemVer → Poetry 风格反向还原版本号内的标记如-rc.1还原为rc1npm2poetrynpm 范围 → Poetry 范围把空格分隔的 npm 范围还原为逗号分隔的 Poetry 写法以matches(version, range)为例调用链是function matches(version: string, range: string): boolean { const semverVersion poetry2semver(version); return !!( isVersion(version) semverVersion npm.matches(semverVersion, poetry2npm(range)) ); }见 lib/modules/versioning/poetry/index.ts 第 89–96 行也就是说先校验并转换版本号再转换范围最终委托给 npm 版本解析实现lib/modules/versioning/npm/index.ts完成匹配计算。getSatisfyingVersion、minSatisfyingVersion也是同样的模式把候选版本列表批量poetry2semver、把范围poetry2npm调用 npm 实现后再把结果semver2poetry还原成 Poetry 风格输出。一个值得注意的分工isGreaterThan版本间大小比较与sortVersions排序并未走 npm 通道而是直接委托给 pep440 模块index.ts 第 55–57 行、第 241–243 行。从源码结构看这是有意为之——PyPI 返回的版本是 PEP 440 表示直接用它比较能避免转换过程中丢失 PEP 440 的排序语义例如 post 与 local 版本的相对顺序。版本识别VERSION_PATTERN 正则版本是否“可识别”由 lib/modules/versioning/poetry/patterns.ts 中的VERSION_PATTERN决定。该正则的注释写明它是poetry.core.version.Version用于解析“SemVerpre/post/dev 子集与 PEP 440 并集”的等价模式。其结构按 PEP 440 的段序组织可选v前缀v1.2.3合法epoch(?epoch[0-9])!形式如1!2.0.0release 段[0-9](?:\.[0-9]){0,2}即 1 到 3 段数字1、1.9、1.9.4均可pre 段字母标记为a|b|c|rc|alpha|beta|pre|preview可带数字如1.9b0、1.9.prepost 段两种形式——-N如1.9-0或字母post|rev|r如1.9.0-post、1.9.0rev3dev 段dev标记如1.9.0dev0local 段后的本地标识如1.0abc.5。测试用例lib/modules/versioning/poetry/index.spec.ts 中isVersion组验证了边界17.04.01合法前导零会被解析时去掉17.b4.0非法release 段必须是数字0.98.5.1非法超过 3 段。同一文件还定义了RANGE_COMPARATOR_PATTERN匹配范围中的比较运算符^、~、、、、||等用于把范围字符串按运算符切分。版本双向转换poetry2semver 与 semver2poetrypoetry2semvertransform.ts 第 43–82 行做了四件事解析与归一化通过parseLetterTag把 Poetry 的标记拼写归一化为 npm/SemVer 习惯——alpha→a、beta→b、c/pre/preview→rc、r/rev→post缺省的数字默认补0补齐 release 段默认padRelease true时把1、1.9补成1.0.0、1.9.0范围转换时传false不补零去前导零release 各段与 pre/post/dev 数字中的前导零被剥离1.9.01b01→1.9.1-beta.1用semver.valid兜底校验转不出合法 SemVer 就返回null。文档中特别注明的一个设计取舍epoch 段被静默丢弃因为 SemVer 没有对应概念。这意味着1!2.0.0与2.0.0在该模块看来等价属于已知的表达力损失。反向的semver2poetry做对称的还原第 85–101 行把 SemVer 的 pre 标记拼写映射回 Poetry 风格a→alpha、b→beta、c→rc、dev→alpha。测试用例equals(1.9b0, 1.9.0-beta.0) true与getSatisfyingVersion([0.8.0a2,0.8.0a7], ^0.8.0-alpha.0) 0.8.0-alpha.2输出被还原回 alpha 风格都印证了这一往返转换的一致性。范围转换poetry2npm 与 npm2poetryPoetry 与 npm 的范围写法差异主要有两点AND 的分隔符Poetry 用逗号npm 用空格与无运算符版本的含义Poetry 中裸版本号表示“精确匹配”不像 Cargo 隐式补^——源码注释明确说明poetry2npm“doesnt add a^”。poetry2npmtransform.ts 第 110–132 行的流程逗号变空格 → 按RANGE_COMPARATOR_PATTERN切分 → 每段poetry2semver(chunk, false)转换 → 拼接并把替换为。它还带一个throwOnUnsupported开关当范围含!排除如2.6, !3.0.*, 4时抛出异常因为这类模式在 Poetry/npm 之间难以可靠翻译。isValid正是利用这一点返回falseindex.ts 第 68–82 行测试用例isValid(2.6, !3.0.*, !3.1.*, !3.2.*, 4) false验证了这一边界。npm2poetry第 141–163 行做反向拼接先把范围内嵌的版本逐段semver2poetry还原拼写再把运算符与其后的版本粘合避免^ 1.0被空格拆开最终以逗号连接||保留为 Poetry 支持的“或”写法。源码注释指出该函数“largely copied from cargo versioning code”因为两者都使用逗号作 AND 分隔符。完整 API 行为一览api对象index.ts 第 249–267 行导出的全部能力及其实现路径如下API实现方式equals/getMajor/getMinor/getPatchpoetry2semver后委托 npm 实现isVersion/isCompatible直接匹配VERSION_PATTERNisGreaterThan/sortVersions委托 pep440 模块isValidpoetry2npm(input, true)后委托npm.isValid不支持的输入记录 debug 日志并返回falsematches/isLessThanRange版本与范围双向转换后委托 npm 实现getSatisfyingVersion/minSatisfyingVersion批量转换 → npm 计算 →semver2poetry还原isStable转换后委托npm.isStable测试确认1.9.4-beta、1.9.4a0均判为不稳定isSingleVersion1.2.3可带空格或裸版本号视为单版本1.*不算subset范围poetry2npm后委托npm.subset测试确认^1.1.0 || ^2.0.0是^1.0.0 || ^2.0.0的子集getNewValue见下节getNewValue生成依赖约束新值的核心逻辑getNewValue是 Renovate 更新 PR 时计算pyproject.toml新约束值的关键函数index.ts 第 161–239 行其处理顺序为replace 策略下的兼容短路若新版本的 SemVer 形式满足当前约束npm.matches则原样返回currentValue——不满足才改写^/~短写补全handleShort根据当前约束的段数决定补写到哪一级。^1.0.0 新 major 生成^2.0.0而^12.1.7bump 策略生成^2.1.7。测试用例getNewValue(^1.0.0, replace, 1.0.0, 2.0.7) ^2.0.0与getNewValue(^1, bump, 1.0.0, 2.1.7) ^2.1.7分别覆盖了这两种路径完整版号校验若newVersion不是三段完整版本如含 pre 标记或只有两段则放弃计算直接返回原值6.b0.0这类非法号会触发该分支测试getNewValue(5.0,bump,5.0.0,6.b0.0) 5.0验证了这一点委托 npm 计算poetry2npm(currentValue)poetry2semver(newVersion)后调用npm.getNewValue再把结果npm2poetry还原全程 try/catch 保护失败则回退原值。测试用例index.spec.ts 第 207–267 行展示了典型行为可归纳为几类// 精确版本 bump1.0.0 前缀与空格被规范化 getNewValue( 1.0.0, bump, 1.0.0, 1.1.0) 1.1.0 // 通配符范围 getNewValue(1.0.*, replace, 1.0.0, 1.1.0) 1.1.* getNewValue(1.*, replace, 1.0.0, 2.1.0) 2.* // 比较运算符 getNewValue(1.3.4, replace, 1.2.3, 1.5.0) 1.5.1 getNewValue( 1.3.4, replace, 1.2.3, 1.5.0) 1.5.0 // widen 策略追加新的 major 分支而非替换 getNewValue(^2.2, widen, 2.2.0, 3.0.0) ^2.2 || ^3.0.0 getNewValue(^2.2 || ^3.0.0, widen, 3.0.0, 4.0.0) ^2.2 || ^3.0.0 || ^4.0.0 // 预发布版本拼写还原 getNewValue(^1, bump, 1.0.0, 1.0.7rc.1) ^1.0.7-rc.1 getNewValue(^0.8.0-alpha.0, bump, 0.8.0-alpha.0, 0.8.0a1) ^0.8.0-alpha.1注意最后一条输入的新版本0.8.0a1PEP 440 风格输出的约束写作-alpha.1说明npm2poetry的还原映射把 SemVer 的a标记映射回了alpha拼写。模块在 Renovate 中的消费方式该版本解析模块通过模块注册机制lib/modules/versioning/api.ts 自动聚合各子模块导出对外暴露Renovate 配置中以versioning: poetry选用。仓库内的直接消费方包括Poetry 包管理器lib/modules/manager/poetry/schema.ts 导入poetryVersioning用于解析pyproject.toml中 Poetry 依赖的版本约束该 manager 的 zod schema 同时处理 path/git 依赖、PEP 508 风格约束等版本查找过滤器lib/workers/repository/process/lookup/filter.ts 第 150 行附近对config.versioning poetryVersioning.id做特判在过滤候选版本时按 Poetry 规则处理。从源码结构看manager 层与 lookup 流程均把“判断版本关系”的决策下沉到该模块保证了 Python/Poetry 生态的版本语义与 Renovate 通用流程解耦。已知边界与实现取舍基于源码与测试用例可以确认的边界!排除约束不受支持isValid对含!的范围返回falseRenovate 会视为无法解析该约束见 transform.ts 第 123–130 行epoch 段被丢弃poetry2semver注释明确说明SemVer 无 epoch 等价物非完整版本号的 newVersion 不计算新值直接回退原值并记录 debug 日志裸版本号在范围中表示精确匹配不隐式补^与 Cargo 版本文法不同pre/post/dev 的语义依赖转换文档明确指出这些版本在 PEP 440 与 SemVer 表示中不完全等价模块通过统一的归一化拼写表parseLetterTag/semver2poetry保证两端行为一致但诸如 local 版本abc.5的比较最终依赖 pep440 通道。参考文件模块说明文档lib/modules/versioning/poetry/readme.mdAPI 实现lib/modules/versioning/poetry/index.ts版本/范围正则lib/modules/versioning/poetry/patterns.ts双向转换实现lib/modules/versioning/poetry/transform.ts完整测试用例lib/modules/versioning/poetry/index.spec.ts相关模块pep440 版本解析、npm 版本解析、poetry 包管理器版本模块注册入口lib/modules/versioning/index.ts【免费下载链接】renovateHome of the Renovate CLI: Cross-platform Dependency Automation by Mend.io项目地址: https://gitcode.com/GitHub_Trending/re/renovate创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表