Skip to content

First implementation of next_version #222

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
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
28 changes: 26 additions & 2 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
------------------

Expand Down
75 changes: 74 additions & 1 deletion semver.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -898,6 +948,7 @@ def replace(version, **parts):
return str(VersionInfo.parse(version).replace(**parts))


# ---- CLI
def cmd_bump(args):
"""
Subcommand: Bumps a version.
Expand Down Expand Up @@ -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 <VERSION> <PART>

: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.
Expand Down Expand Up @@ -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


Expand Down
36 changes: 36 additions & 0 deletions test_semver.py
Original file line number Diff line number Diff line change
Expand Up @@ -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