From 5b049605c8cba0a6f66cb5830955c95789d20ef9 Mon Sep 17 00:00:00 2001 From: Tom Schraitle Date: Sun, 23 Feb 2020 15:25:16 +0100 Subject: [PATCH] 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 Co-authored-By: Karol --- docs/usage.rst | 28 +++++++++++++++++-- semver.py | 75 +++++++++++++++++++++++++++++++++++++++++++++++++- test_semver.py | 36 ++++++++++++++++++++++++ 3 files changed, 136 insertions(+), 3 deletions(-) diff --git a/docs/usage.rst b/docs/usage.rst index 91e4d069..fd2af5fa 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -244,8 +244,8 @@ It is possible to convert a :class:`semver.VersionInfo` instance: (5, 4, 2, None, None) -Increasing Parts of a Version ------------------------------ +Raising Parts of a Version +-------------------------- The ``semver`` module contains the following functions to raise parts of a version: @@ -276,6 +276,30 @@ a version: Likewise the module level functions :func:`semver.bump_major`. +Increasing Parts of a Version Taking into Account Prereleases +------------------------------------------------------------- + +.. versionadded:: 2.10.0 + Added :func:`semver.VersionInfo.next_version`. + +If you want to raise your version and take prereleases into account, +the function :func:`semver.VersionInfo.next_version` would perhaps a +better fit. + + +.. code-block:: python + + >>> v = semver.VersionInfo.parse("3.4.5-pre.2+build.4") + >>> str(v.next_version(part="prerelease")) + '3.4.5-pre.3' + >>> str(semver.VersionInfo.parse("3.4.5-pre.2+build.4").next_version(part="patch")) + '3.4.5' + >>> str(semver.VersionInfo.parse("3.4.5+build.4").next_version(part="patch")) + '3.4.5' + >>> str(semver.VersionInfo.parse("0.1.4").next_version("prerelease")) + '0.1.5-rc.1' + + Comparing Versions ------------------ diff --git a/semver.py b/semver.py index a5739b16..35192ed9 100644 --- a/semver.py +++ b/semver.py @@ -422,6 +422,53 @@ def compare(self, other): return rccmp + def next_version(self, part, prerelease_token="rc"): + """ + Determines next version, preserving natural order. + + .. versionadded:: 2.10.0 + + This function is taking prereleases into account. + The "major", "minor", and "patch" raises the respective parts like + the ``bump_*`` functions. The real difference is using the + "preprelease" part. It gives you the next patch version of the prerelease, + for example: + + >>> str(semver.VersionInfo.parse("0.1.4").next_version("prerelease")) + '0.1.5-rc.1' + + :param part: One of "major", "minor", "patch", or "prerelease" + :param prerelease_token: prefix string of prerelease, defaults to 'rc' + :return: + """ + validparts = { + "major", + "minor", + "patch", + "prerelease", + # "build", # currently not used + } + if part not in validparts: + raise ValueError( + "Invalid part. Expected one of {validparts}, but got {part!r}".format( + validparts=validparts, part=part + ) + ) + version = self + if (version.prerelease or version.build) and ( + part == "patch" + or (part == "minor" and version.patch == 0) + or (part == "major" and version.minor == version.patch == 0) + ): + return version.replace(prerelease=None, build=None) + + if part in ("major", "minor", "patch"): + return str(getattr(version, "bump_" + part)()) + + if not version.prerelease: + version = version.bump_patch() + return version.bump_prerelease(prerelease_token) + @comparator def __eq__(self, other): return self.compare(other) == 0 @@ -709,7 +756,10 @@ def max_ver(ver1, ver2): >>> semver.max_ver("1.0.0", "2.0.0") '2.0.0' """ - ver1 = VersionInfo.parse(ver1) + if isinstance(ver1, str): + ver1 = VersionInfo.parse(ver1) + elif not isinstance(ver1, VersionInfo): + raise TypeError() cmp_res = ver1.compare(ver2) if cmp_res >= 0: return str(ver1) @@ -898,6 +948,7 @@ def replace(version, **parts): return str(VersionInfo.parse(version).replace(**parts)) +# ---- CLI def cmd_bump(args): """ Subcommand: Bumps a version. @@ -953,6 +1004,19 @@ def cmd_compare(args): return str(compare(args.version1, args.version2)) +def cmd_nextver(args): + """ + Subcommand: Determines the next version, taking prereleases into account. + + Synopsis: nextver + + :param args: The parsed arguments + :type args: :class:`argparse.Namespace` + """ + version = VersionInfo.parse(args.version) + return str(version.next_version(args.part)) + + def createparser(): """ Create an :class:`argparse.ArgumentParser` instance. @@ -995,6 +1059,15 @@ def createparser(): parser_check.set_defaults(func=cmd_check) parser_check.add_argument("version", help="Version to check") + # Create the nextver subcommand + parser_nextver = s.add_parser( + "nextver", help="Determines the next version, taking prereleases into account." + ) + parser_nextver.set_defaults(func=cmd_nextver) + parser_nextver.add_argument("version", help="Version to raise") + parser_nextver.add_argument( + "part", help="One of 'major', 'minor', 'patch', or 'prerelease'" + ) return parser diff --git a/test_semver.py b/test_semver.py index 5daf3f1a..8ac42c81 100644 --- a/test_semver.py +++ b/test_semver.py @@ -881,3 +881,39 @@ def mock_func(): with pytest.deprecated_call(): assert mock_func() + + +def test_next_version_with_invalid_parts(): + version = VersionInfo.parse("1.0.1") + with pytest.raises(ValueError): + version.next_version("invalid") + + +@pytest.mark.parametrize( + "version, part, expected", + [ + # major + ("1.0.4-rc.1", "major", "2.0.0"), + ("1.1.0-rc.1", "major", "2.0.0"), + ("1.1.4-rc.1", "major", "2.0.0"), + ("1.2.3", "major", "2.0.0"), + ("1.0.0-rc.1", "major", "1.0.0"), + # minor + ("0.2.0-rc.1", "minor", "0.2.0"), + ("0.2.5-rc.1", "minor", "0.3.0"), + ("1.3.1", "minor", "1.4.0"), + # patch + ("1.3.2", "patch", "1.3.3"), + ("0.1.5-rc.2", "patch", "0.1.5"), + # prerelease + ("0.1.4", "prerelease", "0.1.5-rc.1"), + ("0.1.5-rc.1", "prerelease", "0.1.5-rc.2"), + # special cases + ("0.2.0-rc.1", "patch", "0.2.0"), # same as "minor" + ("1.0.0-rc.1", "patch", "1.0.0"), # same as "major" + ("1.0.0-rc.1", "minor", "1.0.0"), # same as "major" + ], +) +def test_next_version_with_versioninfo(version, part, expected): + ver = VersionInfo.parse(version) + assert str(ver.next_version(part)) == expected 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