Fix bump_build() silently returning an unchanged version for digitless build metadata - #480
Merged
Merged
Conversation
Str0k
marked this pull request as ready for review
September 12, 2026 11:32
Member
|
Thanks, looks good. 👍 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Calling
Version.parse("1.2.3+alpha").bump_build()returned a version whose build metadata was still "alpha" — an identical version object string-wise, despite bump_build() being documented to raise the build part. The private helper_increment_string()leaves a string without any digits unchanged, so a build like "alpha" (no numeric identifier anywhere) was passed through untouched.bump_build()now detects that case and appends ".0" ("1.2.3+alpha"->"1.2.3+alpha.0"), exactly mirroring how PR #460 fixed the analogous prerelease case ("3.4.5-rc9"->"3.4.5-rc9.0"). Numeric-ending builds keep the existing incrementing behavior ("1.2.3+build.9"->"1.2.3+build.10"). Found while investigating #466 (passing the bump token parameter); the CLI--tokenoption itself is already covered by open PR #467 and is intentionally not duplicated here.Implementation reason:
_increment_string()only increments the last numeric sequence and returns the input unchanged when there is none; for the prerelease part this hole was already fixed in _increment_prerelease by appending".0"so the bumped version is a new, higher build step.bump_build()had no such guard, so for digitless build metadata it silently returned an equal version, breaking the documented raise contract and making repeated bumps indistinguishable from no-ops. The minimal fix reuses the same ".0"-append strategy at thebump_build()call site, leaving numeric behavior and the token-constructs-new-build behavior untouched.Validation: a new regression test fails on the unchanged base and passes with this patch; reproduced in an independent clean checkout.
Full-suite results (including pre-existing failures, if any):
{ "base": { "exit": 1, "tests": 393, "failures": 5, "errors": 0, "skipped": 0, "failed_cases": [ "tests.test_bump_build_digitless.test_bump_build_digitless_metadata_should_append_0", "tests.test_bump_build_digitless.test_bump_build_digitless_metadata_should_return_new_version", "tests.test_bump_build_digitless.test_bump_build_digitless_multi_identifier_should_append_0", "tests.test_bump_build_digitless.test_bump_build_token_ignored_for_existing_digitless_build", "tests.test_bump_build_digitless.test_bump_build_digitless_bump_is_repeatable" ] }, "patch": { "exit": 0, "tests": 393, "failures": 0, "errors": 0, "skipped": 0, "failed_cases": [] } }AI assistance: implementation and review used Hermes with self-hosted GLM 5.3. Automated test evidence was checked separately. This does not represent a human review.
Additional checks were reproduced on fresh base and patched checkouts:
These diagnostic counts and samples are also present on the unchanged base; comparison of the complete diagnostic sets found no additions.
Pre-existing checker diagnostics (up to five per tool)
{ "mypy": { "total": 1, "sample": [ { "file": "src/semver/version.py", "code": "import-untyped", "message": "Skipping analyzing \"fast_semver_rs_backend\": module is installed, but missing library stubs or py.typed marker" } ] }, "ruff": { "total": 159, "sample": [ { "file": "docs/advanced/coerce.py", "code": "I001", "message": "Import block is un-sorted or un-formatted" }, { "file": "docs/advanced/coerce.py", "code": "UP035", "message": "`typing.Tuple` is deprecated, use `tuple` instead" }, { "file": "docs/advanced/coerce.py", "code": "UP006", "message": "Use `tuple` instead of `Tuple` for type annotation" }, { "file": "docs/advanced/coerce.py", "code": "UP045", "message": "Use `X | None` for type annotations" }, { "file": "docs/advanced/coerce.py", "code": "RUF100", "message": "Unused `noqa` directive (non-enabled: `E203`)" } ] }, "docformatter": { "total": 24, "sample": [ { "file": "before/src/semver/_deprecated.py", "code": "docformat", "message": "- :param replace: the function to replace (use the full qualified" }, { "file": "before/src/semver/_deprecated.py", "code": "docformat", "message": "- name like ``semver.version.Version.bump_major``." }, { "file": "before/src/semver/_deprecated.py", "code": "docformat", "message": "+ :param replace: the function to replace (use the full qualified name like" }, { "file": "before/src/semver/_deprecated.py", "code": "docformat", "message": "+ ``semver.version.Version.bump_major``." }, { "file": "before/src/semver/_deprecated.py", "code": "docformat", "message": "- :param category: allow you to specify the deprecation warning class" } ] } }Changelog: the reviewed bugfix entry is attached using this PR number after draft creation, following
changelog.d/README.rst. The PR is marked ready only after the fragment passes Towncrier.