Skip to content

Commit f3aa4ab

Browse files
committed
First implementation of next_version
* Implement semver.next_version(version, part, prerelease_token="rc") * Add test cases * test_next_version * test_next_version_with_invalid_parts * Reformat code with black Co-authored-by: George Sakkis <gsakkis>
1 parent 3f92aa5 commit f3aa4ab

File tree

2 files changed

+104
-8
lines changed

2 files changed

+104
-8
lines changed

semver.py

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,26 @@ def bump_build(self, token="build"):
257257
"""
258258
return parse_version_info(bump_build(str(self), token))
259259

260+
def next_version(self, part, prerelease_token="rc"):
261+
"""
262+
Determines the next version, takeing prereleases into account.
263+
264+
The "major", "minor", and "patch" raises the respective parts like
265+
the ``bump_*`` functions. The real difference is using the
266+
"preprelease" part. It gives you the next patch version of the prerelease,
267+
for example:
268+
269+
>>> semver.next_version("0.1.4", "prerelease")
270+
'0.1.5-rc.1'
271+
272+
:param version: a semver version string
273+
:param part: One of "major", "minor", "patch", or "prerelease"
274+
:param prerelease_token: prefix string of prerelease, defaults to 'rc'
275+
:return:
276+
"""
277+
nxt = next_version(str(self), part, prerelease_token)
278+
return parse_version_info(nxt)
279+
260280
@comparator
261281
def __eq__(self, other):
262282
return _compare_by_keys(self._asdict(), _to_dict(other)) == 0
@@ -374,14 +394,7 @@ def parse_version_info(version):
374394
'build.4'
375395
"""
376396
parts = parse(version)
377-
version_info = VersionInfo(
378-
parts["major"],
379-
parts["minor"],
380-
parts["patch"],
381-
parts["prerelease"],
382-
parts["build"],
383-
)
384-
397+
version_info = VersionInfo(**parts)
385398
return version_info
386399

387400

@@ -684,6 +697,53 @@ def finalize_version(version):
684697
return format_version(verinfo["major"], verinfo["minor"], verinfo["patch"])
685698

686699

700+
def next_version(version, part, prerelease_token="rc"):
701+
"""
702+
Determines the next version, takeing prereleases into account.
703+
704+
The "major", "minor", and "patch" raises the respective parts like
705+
the ``bump_*`` functions. The real difference is using the
706+
"preprelease" part. It gives you the next patch version of the prerelease,
707+
for example:
708+
709+
>>> semver.next_version("0.1.4", "prerelease")
710+
'0.1.5-rc.1'
711+
712+
:param version: a semver version string
713+
:param part: One of "major", "minor", "patch", or "prerelease"
714+
:param prerelease_token: prefix string of prerelease, defaults to 'rc'
715+
:return:
716+
"""
717+
validparts = {
718+
"major",
719+
"minor",
720+
"patch",
721+
"prerelease",
722+
# "build", # ???
723+
}
724+
if part not in validparts:
725+
raise ValueError(
726+
"Invalid part. Expected one of {validparts}, but got {part!r}".format(
727+
validparts=validparts, part=part
728+
)
729+
)
730+
version = VersionInfo.parse(version)
731+
if version.prerelease and (
732+
part == "patch"
733+
or (part == "minor" and version.patch == 0)
734+
or (part == "major" and version.minor == version.patch == 0)
735+
):
736+
return str(version.replace(prerelease=None))
737+
738+
if part in ("major", "minor", "patch"):
739+
return str(getattr(version, "bump_" + part)())
740+
741+
if not version.prerelease:
742+
version = version.bump_patch()
743+
return str(version.bump_prerelease(prerelease_token))
744+
745+
746+
# ---- CLI
687747
def cmd_bump(args):
688748
"""
689749
Subcommand: Bumps a version.

test_semver.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
match,
2121
max_ver,
2222
min_ver,
23+
next_version,
2324
parse,
2425
parse_version_info,
2526
process,
@@ -39,6 +40,7 @@
3940
match,
4041
max_ver,
4142
min_ver,
43+
next_version,
4244
parse,
4345
process,
4446
replace,
@@ -827,3 +829,37 @@ def test_replace_raises_ValueError_for_non_numeric_values():
827829
def test_should_versioninfo_isvalid():
828830
assert VersionInfo.isvalid("1.0.0") is True
829831
assert VersionInfo.isvalid("foo") is False
832+
833+
834+
@pytest.mark.parametrize(
835+
"version, part, expected",
836+
[
837+
# major
838+
("1.0.4-rc.1", "major", "2.0.0"),
839+
("1.1.0-rc.1", "major", "2.0.0"),
840+
("1.1.4-rc.1", "major", "2.0.0"),
841+
("1.2.3", "major", "2.0.0"),
842+
("1.0.0-rc.1", "major", "1.0.0"),
843+
# minor
844+
("0.2.0-rc.1", "minor", "0.2.0"),
845+
("0.2.5-rc.1", "minor", "0.3.0"),
846+
("1.3.1", "minor", "1.4.0"),
847+
# patch
848+
("1.3.2", "patch", "1.3.3"),
849+
("0.1.5-rc.2", "patch", "0.1.5"),
850+
# prerelease
851+
("0.1.4", "prerelease", "0.1.5-rc.1"),
852+
("0.1.5-rc.1", "prerelease", "0.1.5-rc.2"),
853+
# special cases
854+
("0.2.0-rc.1", "patch", "0.2.0"), # same as "minor"
855+
("1.0.0-rc.1", "patch", "1.0.0"), # same as "major"
856+
("1.0.0-rc.1", "minor", "1.0.0"), # same as "major"
857+
],
858+
)
859+
def test_next_version(version, part, expected):
860+
assert next_version(version, part) == expected
861+
862+
863+
def test_next_version_with_invalid_parts():
864+
with pytest.raises(ValueError):
865+
next_version("1.0.1", "invalid")

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy