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

Conversation

tomschr
Copy link
Member

@tomschr tomschr commented Feb 23, 2020

This PR contains (based on #221):

  • Implement semver.next_version(version, part, *, prerelease_token="rc")
  • Add test cases

TODOs:

  • Implement semver.VersionInfo.next_version counterpart
  • Describe the function(s) in our documentation

@gsakkis This is a first draft, feel free to comment. 😉

@tomschr tomschr added Enhancement Not a bug, but increases or improves in value, quality, desirability, or attractiveness Release_2.x.y Only for the major release 2 labels Feb 23, 2020
@tomschr tomschr force-pushed the feature/221-bump-prerelease-and-build branch from dee4dff to 55d63b0 Compare February 23, 2020 14:36
@tomschr tomschr linked an issue Feb 23, 2020 that may be closed by this pull request
@tomschr tomschr force-pushed the feature/221-bump-prerelease-and-build branch 2 times, most recently from e43fa78 to 5dcc093 Compare February 23, 2020 14:49
@tomschr tomschr changed the title First implementation of #221 (next_version) First implementation of next_version Feb 23, 2020
@gsakkis
Copy link
Contributor

gsakkis commented Feb 24, 2020

@tomschr here are a few more (currently failing) test cases:

        ("0.1.5-rc.1", "prerelease", "0.1.5-rc.2"),
        ("0.1.5-rc.2", "patch", "0.1.5"),
        ("0.2.0-rc.1", "minor", "0.2.0"),
        ("1.0.0-rc.1", "major", "1.0.0"),

@gsakkis
Copy link
Contributor

gsakkis commented Feb 24, 2020

Open issues:

  1. How to deal with the following cases:
  • ("0.2.0-rc.1", "patch", ???)
  • ("1.0.0-rc.1", "patch", ???)
  • ("1.0.0-rc.1", "minor", ???)
    My suggestion would be to raise a custom exception, e.g. IndeterminableVersionException.
  1. I came across another situation I hadn't thought of before: say you're in v0.1.4 and want to bump to next prerelease version. With the current patch this is v0.1.5-rc.1. This makes sense if you plan for the next non-prerelease version to be a patch upgrade, i.e. v0.1.5. But what if you plan a minor or major upgrade right after v0.1.4? In that case the next prerelease should be v0.2.0-rc1 (for minor) or v1.0.0-rc1 (for major).

Currently there is not enough information to determine which of the three alternatives to choose. It needs either an extra parameter, or since this affects only prereleases, allow two more part values: prerelease-minor and prerelease-major (and perhaps prerelease-patch for consistency, which would be equivalent to prerelease). So in short:

("0.1.4", "prerelease", "0.1.5-rc.1")
("0.1.4", "prerelease-patch", "0.1.5-rc.1") # optional
("0.1.4", "prerelease-minor", "0.2.0-rc.1")
("0.1.4", "prerelease-major", "1.0.0-rc.1")

What do you think?

@tomschr tomschr force-pushed the feature/221-bump-prerelease-and-build branch from 5dcc093 to 99d7660 Compare February 24, 2020 10:39
@tomschr
Copy link
Member Author

tomschr commented Feb 24, 2020

@tomschr here are a few more (currently failing) test cases:
[...]

I've added them to the test file and fixed them. Additionally, I've added you as co-author.

The implementation currently looks horrible as it contains a lot of elif parts. We can improve that later.

@gsakkis
Copy link
Contributor

gsakkis commented Feb 24, 2020

Getting closer :) Four more failing cases:

        ("0.2.5-rc.1", "minor", "0.3.0"),
        ("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"),

@gsakkis
Copy link
Contributor

gsakkis commented Feb 24, 2020

Here's a simplified implementation that passes all 12 cases, feel free to apply the diff:

diff --git a/semver.py b/semver.py
index 67ddded..294d369 100644
--- a/semver.py
+++ b/semver.py
@@ -715,24 +715,19 @@ def next_version(version, part, prerelease_token="rc"):
             )
         )
     version = VersionInfo.parse(version)
-    if part == "major" and version.prerelease:
-        return format_version(version.major, version.minor, version.patch)
-    elif part == "major" and not version.prerelease:
-        return str(version.bump_major())
-    elif part == "minor" and version.prerelease:
-        return format_version(version.major, version.minor, version.patch)
-    elif part == "minor" and not version.prerelease:
-        return str(version.bump_minor())
-    elif part == "patch" and version.prerelease:
-        return format_version(version.major, version.minor, version.patch)
-    elif part == "patch" and not version.prerelease:
-        return str(version.bump_patch())
-    elif part == "prerelease" and version.prerelease:
-        return str(version.bump_prerelease(token=prerelease_token))
-    elif part == "prerelease" and not version.prerelease:
-        return str(version.bump_patch().bump_prerelease(token=prerelease_token))
-
-    raise RuntimeError("Should not happen, got {part}".format(part=part))
+    if version.prerelease and (
+        part == "patch"
+        or (part == "minor" and version.patch == 0)
+        or (part == "major" and version.minor == version.patch == 0)
+    ):
+        return str(version.replace(prerelease=None))
+
+    if part in ("major", "minor", "patch"):
+        return str(getattr(version, "bump_" + part)())
+
+    if not version.prerelease:
+        version = version.bump_patch()
+    return str(version.bump_prerelease(prerelease_token))
 
 
 # ---- CLI

@tomschr tomschr force-pushed the feature/221-bump-prerelease-and-build branch from 99d7660 to 171fc97 Compare February 24, 2020 21:02
@tomschr
Copy link
Member Author

tomschr commented Feb 24, 2020

@gsakkis Brilliant! 👍 Thanks for the contribution! I've included your patches in commit 171fc97. For the test cases, I've sorted them against the parts. Looks better. 😉

@tomschr
Copy link
Member Author

tomschr commented Feb 24, 2020

I'm wondering, if we should check parts specifically for build and give a better error message why build cannot be used. At the moment, it raises a ValueError with an Invalid part message. In that specific case, it may confuse people. I'd suggest to add:

diff --git i/semver.py w/semver.py
index b6443ae..5e69b1b 100644
--- i/semver.py
+++ w/semver.py
@@ -708,6 +708,8 @@ def next_version(version, part, prerelease_token="rc"):
         "prerelease",
         # "build", # ???
     }
+    if part == "build":
+        raise ValueError("The build part is not predictable")

I hadn't had a better idea for the error message. What do you think/prefer?

@tomschr tomschr requested a review from s-celles February 25, 2020 08:22
@gsakkis
Copy link
Contributor

gsakkis commented Feb 25, 2020

I don't mind the generic Invalid part message as it mentions which are the valid parts but I don't have a strong opinion either way, whatever you prefer.

Copy link
Member

@s-celles s-celles left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@tomschr
Copy link
Member Author

tomschr commented Feb 27, 2020

@gsakkis Ok, I have to think again what would be better.

Do you have anything else which needs to be covered in this function? If not, I would implement the rest and extend the documentation. It would be very helpful if you could review it. 😉

@gsakkis
Copy link
Contributor

gsakkis commented Feb 29, 2020

@tomschr would be nice to add the test cases mentioned here. With the current implementation they yield:

  • ("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"

Other than that LGTM too 👍

@tomschr tomschr force-pushed the feature/221-bump-prerelease-and-build branch 3 times, most recently from c787054 to f3aa4ab Compare February 29, 2020 17:03
@tomschr
Copy link
Member Author

tomschr commented Feb 29, 2020

@gsakkis Added your test cases. Thanks! 👍

After looking a bit closer to this class and this function, I realized there are a lot of similarities. As my findings are maybe a bit radical, I've opened a separate issue, see #225 for the details. 😄

For this PR, it isn't relevant.

@tomschr
Copy link
Member Author

tomschr commented Mar 1, 2020

@gsakkis George, during documenting the function, I found something where I need your help.

Consider the following code example:

>>> semver.next_version("3.4.5-pre.2+build.4", part="prerelease")  # (1)
'3.4.5-pre.3'
>>> semver.next_version("3.4.5-pre.2+build.4", part="patch")  # (2)
'3.4.5+build.4
>>> semver.next_version("3.4.5+build.4", part="patch")  # (3)
'3.4.6'

The first one is plausible, but what about the second one? Shouldn't it be 3.4.5? What about the third one, should it be 3.4.5 too? I think it would be weird to have 3.4.5 as a result in (2), but 3.4.6 in (3) just for the build part.

I could fix and remove the build part when I patch your version in the following lines:

diff --git i/semver.py w/semver.py
index ac3bfd1..48241ed 100644
--- i/semver.py
+++ w/semver.py
@@ -728,12 +728,12 @@ def next_version(version, part, prerelease_token="rc"):
             )
         )
     version = VersionInfo.parse(version)
-    if version.prerelease and (
+    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 str(version.replace(prerelease=None))
+        return str(version.replace(prerelease=None, build=None))

Would that be plausible for you? What do you think? (You can look at the latest commit where I've adjusted the tests and applied the diff from above).

I guess, we run into these uncharted area of the build part. 😉

@tomschr tomschr force-pushed the feature/221-bump-prerelease-and-build branch 2 times, most recently from af7e722 to 56864d1 Compare March 1, 2020 16:06
@gsakkis
Copy link
Contributor

gsakkis commented Mar 3, 2020

Would that be plausible for you? What do you think?
I guess, we run into these uncharted area of the build part.

Indeed, afaict the rules regarding the build part are fuzzy but your changes look reasonable, at least more reasonable than preserving the build when bumping, so :shipit:

@tomschr
Copy link
Member Author

tomschr commented Mar 14, 2020

@gsakkis I just remembered, we should probably integrate the functionality into the CLI script pysemver as well, right?

@tomschr tomschr force-pushed the feature/221-bump-prerelease-and-build branch 2 times, most recently from b71d1de to 3c3388d Compare March 14, 2020 19:05
@tomschr
Copy link
Member Author

tomschr commented Mar 14, 2020

I've added now the new subcommand nextver:

$ pysemver nextver 1.2.3 prerelease
1.2.4-rc.1

@tomschr
Copy link
Member Author

tomschr commented Apr 13, 2020

@gsakkis
After looking of all issues, it seems I forgot this one. 😢 Maybe it isn't as bad, as it could simplify the code a bit in the light of #229. Once this is merged, I would like to continue with that.

However, I would avoid a module level function next_version. Instead, I would integrate this as a method only function for the VersionInfo class. Would you be ok with that?

My proposal would be:

    def next_version(self, part:str, prerelease_token:str="rc"):
        """
        Determines the next version, 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: a new VersionInfo instance, it doesn't modify self
        """
        validparts = frozenset([
            "major",
            "minor",
            "patch",
            "prerelease",
        ])
        cls = type(self)
        if part not in validparts:
            raise ValueError(
                "Invalid part. Expected one of {validparts}, but got {part!r}".format(
                    validparts=validparts, part=part
                )
            )
        if (self.prerelease or self.build) and (
            part == "patch"
            or (part == "minor" and self.patch == 0)
            or (part == "major" and self.minor == self.patch == 0)
        ):
            newver = cls(*self.to_tuple())
            return newver.replace(prerelease=None, build=None)

        if part in ("major", "minor", "patch"):
            return getattr(self, "bump_" + part)()

        version = self
        if not self.prerelease:
            version = self.bump_patch()
        return version.bump_prerelease(prerelease_token)

I haven't tested it, but I thought, I write it down to document it.

@gsakkis
Copy link
Contributor

gsakkis commented Apr 13, 2020

However, I would avoid a module level function next_version. Instead, I would integrate this as a method only function for the VersionInfo class. Would you be ok with that?

Yes absolutely, it makes sense.

@tomschr tomschr self-assigned this Apr 17, 2020
@tomschr
Copy link
Member Author

tomschr commented Apr 17, 2020

@gsakkis I've tried to rebase and adjust the changes. However, Python2 emits this error:

$ tox -e py27
[...]
      def iter_versions(self, firstpart, *parts, prerelease_token="rc"):                                                                                                                  
                                                                ^                                                                                                                         
  SyntaxError: invalid syntax

One solution would be to add this for Python3 only and leave Python2 behind. What would you think?

@tomschr tomschr added Release_3.x.y Only for the major release 3 and removed Release_2.x.y Only for the major release 2 labels Apr 19, 2020
@gsakkis
Copy link
Contributor

gsakkis commented Apr 23, 2020

@tomschr did you mean to add this comment on #223? In any case the signature I proposed is Python 2 compatible: def iter_versions(self, parts, prerelease_token="rc")

@tomschr tomschr force-pushed the feature/221-bump-prerelease-and-build branch from 3c3388d to 85d4ff1 Compare April 23, 2020 19:48
@tomschr
Copy link
Member Author

tomschr commented Apr 23, 2020

Ok, it seems I introduced something like def next_version(self, *, parts, prerelease_token="rc") which is only possible in Python3. You are right, your signature is Python2 compatible.

@tomschr tomschr force-pushed the feature/221-bump-prerelease-and-build branch from 85d4ff1 to 477e8f2 Compare April 23, 2020 19:50
@tomschr tomschr removed the Release_3.x.y Only for the major release 3 label Apr 23, 2020
@tomschr
Copy link
Member Author

tomschr commented Apr 23, 2020

@gsakkis I've fixed the conflicts and implemented the next_version in the VersionInfo class. A module level function is not available. Also adapted the test case.

I was wrong, it's not a Python3 only issue, so I removed this label. Maybe we could even incorporate it into the 2.10.0 release.

Locally, all tests of Python 2.7 and 3.6 were successful. However, it would be good to have a final look and check it. That would be great, George! 👍

@tomschr tomschr force-pushed the feature/221-bump-prerelease-and-build branch from 477e8f2 to 6c48f6d Compare April 23, 2020 20:41
@tomschr tomschr mentioned this pull request Apr 25, 2020
5 tasks
@tomschr tomschr force-pushed the feature/221-bump-prerelease-and-build branch from 6c48f6d to c0a1f14 Compare April 27, 2020 18:03
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>
@tomschr tomschr force-pushed the feature/221-bump-prerelease-and-build branch from c0a1f14 to 5b04960 Compare April 27, 2020 20:11
@tomschr
Copy link
Member Author

tomschr commented May 1, 2020

@gsakkis If you don't mind or object, I'd like to merge it soon (Saturday?) to make the 2.10.0 release. 😄

@gsakkis
Copy link
Contributor

gsakkis commented May 1, 2020

@tomschr LGTM!

@tomschr tomschr merged commit 8d4336e into python-semver:master May 1, 2020
@tomschr tomschr deleted the feature/221-bump-prerelease-and-build branch May 1, 2020 12:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhancement Not a bug, but increases or improves in value, quality, desirability, or attractiveness
Projects
None yet
Development

Successfully merging this pull request may close these issues.

bump_prerelease and bump_build return smaller version
4 participants