Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelog.d/497.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
``Version.next_version("prerelease", prerelease_token=...)`` now advances the
patch version when switching tokens would otherwise produce a lower version.
For example, switching from ``1.2.3-rc.1`` to ``alpha`` returns
``1.2.4-alpha.1``. Forward token changes retain the current patch version.
13 changes: 12 additions & 1 deletion src/semver/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,12 @@ def next_version(self, part: str, prerelease_token: str = "rc") -> "Version":
>>> str(semver.Version.parse("0.1.4").next_version("prerelease"))
'0.1.5-rc.1'

Switching to an earlier prerelease token advances the patch version
so the result remains greater than the original version:

>>> str(semver.Version.parse("1.2.3-rc.1").next_version("prerelease", "alpha"))
'1.2.4-alpha.1'

:param part: One of "major", "minor", "patch", or "prerelease"
:param prerelease_token: prefix string of prerelease, defaults to 'rc'
:return: new object with the appropriate part raised
Expand Down Expand Up @@ -562,7 +568,12 @@ def next_version(self, part: str, prerelease_token: str = "rc") -> "Version":
version.prerelease == prerelease_token
or version.prerelease.startswith(prerelease_token + ".")
):
return version.replace(prerelease=f"{prerelease_token}.1", build=None)
candidate = version.replace(
prerelease=f"{prerelease_token}.1", build=None
)
if candidate > version:
return candidate
return version.bump_patch().bump_prerelease(prerelease_token)
return version.bump_prerelease(prerelease_token, bump_when_empty=True)

@_comparator
Expand Down
3 changes: 2 additions & 1 deletion tests/test_bump.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ def test_should_ignore_build_on_prerelease_bump():
("1.2.3-alpha.beta.gamma.1", "alpha", "1.2.3-alpha.beta.gamma.2"),
("1.2.3-rc3", "rc", "1.2.3-rc4"),
("1.2.3-rc.3", "rc", "1.2.3-rc.4"),
("1.2.3-alphaXbeta9", "alpha.beta", "1.2.3-alpha.beta.1"),
("1.2.3-alphaXbeta9", "alpha.beta", "1.2.4-alpha.beta.1"),
],
)
def test_next_version_matches_literal_prerelease_token_family(
Expand All @@ -170,6 +170,7 @@ def test_next_version_matches_literal_prerelease_token_family(
result = original.next_version("prerelease", token)
assert str(result) == expected
assert Version.parse(str(result)).to_tuple() == result.to_tuple()
assert result > original
assert str(original) == version


Expand Down
21 changes: 21 additions & 0 deletions tests/test_issue484.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,29 @@
"""Regression test for issue #484: next_version with old-style prereleases."""

import pytest

from semver import Version


@pytest.mark.parametrize(
"current, token, expected",
[
("1.2.3-rc.1", "alpha", "1.2.4-alpha.1"),
("1.2.3-beta.3", "alpha", "1.2.4-alpha.1"),
("1.2.3-rc.1+build.7", "dev", "1.2.4-dev.1"),
("1.2.3-rc3", "alpha", "1.2.4-alpha.1"),
("1.2.3-rc.1", "2", "1.2.4-2.1"),
("1.2.3-alpha.4", "rc", "1.2.3-rc.1"),
],
)
def test_token_switch_preserves_version_order(current, token, expected):
original = Version.parse(current)
result = original.next_version("prerelease", token)
assert str(result) == expected
assert result > original
assert str(original) == current


def test_issue_484_rc3_to_rc4():
"""Old-style prerelease 'rc3' should bump to 'rc4', not reset to 'rc.1'."""
v = Version.parse("0.1.2-rc3")
Expand Down
Loading