5.1. Dealing with Invalid Versions¶
As semver follows the semver specification, it cannot parse version strings which are considered “invalid” by that specification. The semver library cannot know all the possible variations so you need to help the library a bit.
For example, if you have a version string v1.2
would be an invalid
semver version.
However, “basic” version strings consisting of major, minor,
and patch part, can be easy to convert. The following function extract this
information and returns a tuple with two items:
import re
from semver import Version
from typing import Optional, Tuple
BASEVERSION = re.compile(
r"""[vV]?
(?P<major>0|[1-9]\d*)
(\.
(?P<minor>0|[1-9]\d*)
(\.
(?P<patch>0|[1-9]\d*)
)?
)?
""",
re.VERBOSE,
)
def coerce(version: str) -> Tuple[Version, Optional[str]]:
"""
Convert an incomplete version string into a semver-compatible Version
object
* Tries to detect a "basic" version string (``major.minor.patch``).
* If not enough components can be found, missing components are
set to zero to obtain a valid semver version.
:param str version: the version string to convert
:return: a tuple with a :class:`Version` instance (or ``None``
if it's not a version) and the rest of the string which doesn't
belong to a basic version.
:rtype: tuple(:class:`Version` | None, str)
"""
match = BASEVERSION.search(version)
if not match:
return (None, version)
ver = {
key: 0 if value is None else value for key, value in match.groupdict().items()
}
ver = Version(**ver)
rest = match.string[match.end() :] # noqa:E203
return ver, rest
The function returns a tuple, containing a Version
instance or None as the first element and the rest as the second element.
The second element (the rest) can be used to make further adjustments.
For example:
>>> coerce("v1.2")
(Version(major=1, minor=2, patch=0, prerelease=None, build=None), '')
>>> coerce("v2.5.2-bla")
(Version(major=2, minor=5, patch=2, prerelease=None, build=None), '-bla')