Skip to content

Commit 477e8f2

Browse files
tomschrgsakkisppkt
committed
Implement of VersionInfo.next_version() function
Synopsis: semver.VersionInfo.next_version(version, part, prerelease_token="rc") * Add test cases * test_next_version * test_next_version_with_invalid_parts * Reformat code with black * Document it in usage.rst * Implement "nextver" subcommand for pysemver command Co-authored-by: George Sakkis <gsakkis@users.noreply.github.com> Co-authored-By: Karol <ppkt@users.noreply.github.com>
1 parent d69e7c4 commit 477e8f2

File tree

3 files changed

+129
-2
lines changed

3 files changed

+129
-2
lines changed

docs/usage.rst

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,8 +244,8 @@ It is possible to convert a :class:`semver.VersionInfo` instance:
244244
(5, 4, 2, None, None)
245245

246246

247-
Increasing Parts of a Version
248-
-----------------------------
247+
Raising Parts of a Version
248+
--------------------------
249249

250250
The ``semver`` module contains the following functions to raise parts of
251251
a version:
@@ -276,6 +276,30 @@ a version:
276276
Likewise the module level functions :func:`semver.bump_major`.
277277

278278

279+
Increasing Parts of a Version Taking into Account Prereleases
280+
-------------------------------------------------------------
281+
282+
.. versionadded:: 2.9.2
283+
Added :func:`semver.VersionInfo.next_version`.
284+
285+
If you want to raise your version and take prereleases into account,
286+
the function :func:`semver.VersionInfo.next_version` would perhaps a
287+
better fit.
288+
289+
290+
.. code-block:: python
291+
292+
>>> v = semver.VersionInfo.parse("3.4.5-pre.2+build.4")
293+
>>> str(v.next_version(part="prerelease"))
294+
'3.4.5-pre.3'
295+
>>> str(semver.VersionInfo.parse("3.4.5-pre.2+build.4").next_version(part="patch"))
296+
'3.4.5'
297+
>>> str(semver.VersionInfo.parse("3.4.5+build.4").next_version(part="patch"))
298+
'3.4.5'
299+
>>> str(semver.VersionInfo.parse("0.1.4").next_version("prerelease"))
300+
'0.1.5-rc.1'
301+
302+
279303
Comparing Versions
280304
------------------
281305

semver.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,50 @@ def compare(self, other):
422422

423423
return rccmp
424424

425+
def next_version(self, part, prerelease_token="rc"):
426+
"""
427+
Determines the next version, taking prereleases into account.
428+
429+
The "major", "minor", and "patch" raises the respective parts like
430+
the ``bump_*`` functions. The real difference is using the
431+
"preprelease" part. It gives you the next patch version of the prerelease,
432+
for example:
433+
434+
>>> str(semver.VersionInfo.parse("0.1.4").next_version("prerelease"))
435+
'0.1.5-rc.1'
436+
437+
:param part: One of "major", "minor", "patch", or "prerelease"
438+
:param prerelease_token: prefix string of prerelease, defaults to 'rc'
439+
:return:
440+
"""
441+
validparts = {
442+
"major",
443+
"minor",
444+
"patch",
445+
"prerelease",
446+
# "build", # currently not used
447+
}
448+
if part not in validparts:
449+
raise ValueError(
450+
"Invalid part. Expected one of {validparts}, but got {part!r}".format(
451+
validparts=validparts, part=part
452+
)
453+
)
454+
version = self
455+
if (version.prerelease or version.build) and (
456+
part == "patch"
457+
or (part == "minor" and version.patch == 0)
458+
or (part == "major" and version.minor == version.patch == 0)
459+
):
460+
return version.replace(prerelease=None, build=None)
461+
462+
if part in ("major", "minor", "patch"):
463+
return str(getattr(version, "bump_" + part)())
464+
465+
if not version.prerelease:
466+
version = version.bump_patch()
467+
return version.bump_prerelease(prerelease_token)
468+
425469
@comparator
426470
def __eq__(self, other):
427471
return self.compare(other) == 0
@@ -898,6 +942,7 @@ def replace(version, **parts):
898942
return str(VersionInfo.parse(version).replace(**parts))
899943

900944

945+
# ---- CLI
901946
def cmd_bump(args):
902947
"""
903948
Subcommand: Bumps a version.
@@ -953,6 +998,19 @@ def cmd_compare(args):
953998
return str(compare(args.version1, args.version2))
954999

9551000

1001+
def cmd_nextver(args):
1002+
"""
1003+
Subcommand: Determines the next version, taking prereleases into account.
1004+
1005+
Synopsis: nextver <VERSION> <PART>
1006+
1007+
:param args: The parsed arguments
1008+
:type args: :class:`argparse.Namespace`
1009+
"""
1010+
version = VersionInfo.parse(args.version)
1011+
return str(version.next_version(args.part))
1012+
1013+
9561014
def createparser():
9571015
"""
9581016
Create an :class:`argparse.ArgumentParser` instance.
@@ -995,6 +1053,15 @@ def createparser():
9951053
parser_check.set_defaults(func=cmd_check)
9961054
parser_check.add_argument("version", help="Version to check")
9971055

1056+
# Create the nextver subcommand
1057+
parser_nextver = s.add_parser(
1058+
"nextver", help="Determines the next version, taking prereleases into account."
1059+
)
1060+
parser_nextver.set_defaults(func=cmd_nextver)
1061+
parser_nextver.add_argument("version", help="Version to raise")
1062+
parser_nextver.add_argument(
1063+
"part", help="One of 'major', 'minor', 'patch', or 'prerelease'"
1064+
)
9981065
return parser
9991066

10001067

test_semver.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -881,3 +881,39 @@ def mock_func():
881881

882882
with pytest.deprecated_call():
883883
assert mock_func()
884+
885+
886+
def test_next_version_with_invalid_parts():
887+
version = VersionInfo.parse("1.0.1")
888+
with pytest.raises(ValueError):
889+
version.next_version("invalid")
890+
891+
892+
@pytest.mark.parametrize(
893+
"version, part, expected",
894+
[
895+
# major
896+
("1.0.4-rc.1", "major", "2.0.0"),
897+
("1.1.0-rc.1", "major", "2.0.0"),
898+
("1.1.4-rc.1", "major", "2.0.0"),
899+
("1.2.3", "major", "2.0.0"),
900+
("1.0.0-rc.1", "major", "1.0.0"),
901+
# minor
902+
("0.2.0-rc.1", "minor", "0.2.0"),
903+
("0.2.5-rc.1", "minor", "0.3.0"),
904+
("1.3.1", "minor", "1.4.0"),
905+
# patch
906+
("1.3.2", "patch", "1.3.3"),
907+
("0.1.5-rc.2", "patch", "0.1.5"),
908+
# prerelease
909+
("0.1.4", "prerelease", "0.1.5-rc.1"),
910+
("0.1.5-rc.1", "prerelease", "0.1.5-rc.2"),
911+
# special cases
912+
("0.2.0-rc.1", "patch", "0.2.0"), # same as "minor"
913+
("1.0.0-rc.1", "patch", "1.0.0"), # same as "major"
914+
("1.0.0-rc.1", "minor", "1.0.0"), # same as "major"
915+
],
916+
)
917+
def test_next_version_with_versioninfo(version, part, expected):
918+
ver = VersionInfo.parse(version)
919+
assert str(ver.next_version(part)) == expected

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