@@ -422,6 +422,53 @@ def compare(self, other):
422422
423423 return rccmp
424424
425+ def next_version (self , part , prerelease_token = "rc" ):
426+ """
427+ Determines next version, preserving natural order.
428+
429+ .. versionadded:: 2.10.0
430+
431+ This function is taking prereleases into account.
432+ The "major", "minor", and "patch" raises the respective parts like
433+ the ``bump_*`` functions. The real difference is using the
434+ "preprelease" part. It gives you the next patch version of the prerelease,
435+ for example:
436+
437+ >>> str(semver.VersionInfo.parse("0.1.4").next_version("prerelease"))
438+ '0.1.5-rc.1'
439+
440+ :param part: One of "major", "minor", "patch", or "prerelease"
441+ :param prerelease_token: prefix string of prerelease, defaults to 'rc'
442+ :return:
443+ """
444+ validparts = {
445+ "major" ,
446+ "minor" ,
447+ "patch" ,
448+ "prerelease" ,
449+ # "build", # currently not used
450+ }
451+ if part not in validparts :
452+ raise ValueError (
453+ "Invalid part. Expected one of {validparts}, but got {part!r}" .format (
454+ validparts = validparts , part = part
455+ )
456+ )
457+ version = self
458+ if (version .prerelease or version .build ) and (
459+ part == "patch"
460+ or (part == "minor" and version .patch == 0 )
461+ or (part == "major" and version .minor == version .patch == 0 )
462+ ):
463+ return version .replace (prerelease = None , build = None )
464+
465+ if part in ("major" , "minor" , "patch" ):
466+ return str (getattr (version , "bump_" + part )())
467+
468+ if not version .prerelease :
469+ version = version .bump_patch ()
470+ return version .bump_prerelease (prerelease_token )
471+
425472 @comparator
426473 def __eq__ (self , other ):
427474 return self .compare (other ) == 0
@@ -709,7 +756,10 @@ def max_ver(ver1, ver2):
709756 >>> semver.max_ver("1.0.0", "2.0.0")
710757 '2.0.0'
711758 """
712- ver1 = VersionInfo .parse (ver1 )
759+ if isinstance (ver1 , str ):
760+ ver1 = VersionInfo .parse (ver1 )
761+ elif not isinstance (ver1 , VersionInfo ):
762+ raise TypeError ()
713763 cmp_res = ver1 .compare (ver2 )
714764 if cmp_res >= 0 :
715765 return str (ver1 )
@@ -898,6 +948,7 @@ def replace(version, **parts):
898948 return str (VersionInfo .parse (version ).replace (** parts ))
899949
900950
951+ # ---- CLI
901952def cmd_bump (args ):
902953 """
903954 Subcommand: Bumps a version.
@@ -953,6 +1004,19 @@ def cmd_compare(args):
9531004 return str (compare (args .version1 , args .version2 ))
9541005
9551006
1007+ def cmd_nextver (args ):
1008+ """
1009+ Subcommand: Determines the next version, taking prereleases into account.
1010+
1011+ Synopsis: nextver <VERSION> <PART>
1012+
1013+ :param args: The parsed arguments
1014+ :type args: :class:`argparse.Namespace`
1015+ """
1016+ version = VersionInfo .parse (args .version )
1017+ return str (version .next_version (args .part ))
1018+
1019+
9561020def createparser ():
9571021 """
9581022 Create an :class:`argparse.ArgumentParser` instance.
@@ -995,6 +1059,15 @@ def createparser():
9951059 parser_check .set_defaults (func = cmd_check )
9961060 parser_check .add_argument ("version" , help = "Version to check" )
9971061
1062+ # Create the nextver subcommand
1063+ parser_nextver = s .add_parser (
1064+ "nextver" , help = "Determines the next version, taking prereleases into account."
1065+ )
1066+ parser_nextver .set_defaults (func = cmd_nextver )
1067+ parser_nextver .add_argument ("version" , help = "Version to raise" )
1068+ parser_nextver .add_argument (
1069+ "part" , help = "One of 'major', 'minor', 'patch', or 'prerelease'"
1070+ )
9981071 return parser
9991072
10001073
0 commit comments