Source code for versions.version
import re
from .errors import Error
from .compat import cmp
# Regular expression used to parse versions.
# It parses semantic versions and tries to normalize not semantic versions
# into semantic ones.
RE = re.compile("""
^
(?P<major>\d+)
(?:
\.
(?P<minor>\d+)
(?:
\.
(?P<patch>\d+)
)?
)?
(?:
-
(?P<prerelease>[0-9a-zA-Z.-]*)
)?
(?:
\+
(?P<build_metadata>[0-9a-zA-Z.-]*)
)?
$
""", re.X)
def get_prerelease_type_precedence(prerelease):
if prerelease is None:
return 2
elif isinstance(prerelease, str):
return 1
elif isinstance(prerelease, int):
return 0
else:
raise TypeError(prerelease)
[docs]class InvalidVersionExpression(Error):
"""Raised when failing to parse a
:ref:`version expression <version-expressions>`.
"""
def __init__(self, version_expression):
#: The bogus version expression.
self.version_expression = version_expression
message = 'Invalid version expression: %r' % version_expression
super(InvalidVersionExpression, self).__init__(message)
[docs]class Version(object):
"""A package version.
:param int major: Version major number
:param int minor: Version minor number
:param int patch: Version patch number
:param prerelease: Version prerelease
:type prerelease: ``str``, ``int`` or ``None``
:param build_metadata: Version build metadata
:type build_metadata: ``None`` or ``str``
This class constructor is usually not called directly.
For version string parsing, see ``Version.parse``.
"""
def __init__(self, major, minor=0, patch=0, prerelease=None,
build_metadata=None):
#: Version major number
self.major = major
#: Version minor number
self.minor = minor
#: Version patch number
self.patch = patch
#: Version prerelease
self.prerelease = prerelease
#: Version build metadata
self.build_metadata = build_metadata
def __hash__(self):
return hash(self.major) ^ hash(self.minor) ^ hash(self.patch) ^ \
hash(self.prerelease) ^ hash(self.build_metadata)
@classmethod
[docs] def parse(cls, version_string):
"""Parses a ``version_string`` and returns a :py:class:`~Version`
object.
"""
match = RE.match(version_string)
if match:
major_str, minor_str, patch_str, prerelease_str, \
build_metadata = match.groups()
major = int(major_str)
if minor_str:
minor = int(minor_str)
else:
minor = 0
if patch_str:
patch = int(patch_str)
else:
patch = 0
if prerelease_str:
try:
prerelease = int(prerelease_str)
except ValueError:
prerelease = prerelease_str
else:
prerelease = None
return cls(major, minor, patch, prerelease, build_metadata)
else:
raise InvalidVersionExpression(version_string)
def __cmp__(self, other):
if isinstance(other, str):
other = Version.parse(other)
if not isinstance(other, Version):
raise TypeError(other)
major_cmp = cmp(self.major, other.major)
if major_cmp == 0:
minor_cmp = cmp(self.minor, other.minor)
if minor_cmp == 0:
patch_cmp = cmp(self.patch, other.patch)
if patch_cmp == 0:
prerelease_t_cmp = cmp(
get_prerelease_type_precedence(self.prerelease),
get_prerelease_type_precedence(other.prerelease))
if prerelease_t_cmp == 0:
if self.prerelease is None:
return 0
else:
return cmp(self.prerelease, other.prerelease)
else:
return prerelease_t_cmp
else:
return patch_cmp
else:
return minor_cmp
else:
return major_cmp
def __eq__(self, other):
return self.__cmp__(other) == 0
def __ne__(self, other):
return self.__cmp__(other) != 0
def __lt__(self, other):
return self.__cmp__(other) < 0
def __gt__(self, other):
return self.__cmp__(other) > 0
def __le__(self, other):
return self.__cmp__(other) <= 0
def __ge__(self, other):
return self.__cmp__(other) >= 0
def __str__(self):
"""Convert version objects to strings::
>>> str(Version.parse('1.0.0')) == '1.0.0'
True
>>> str(Version.parse('1.0')) == '1.0.0'
True
"""
version = '%i.%i.%i' % (self.major, self.minor, self.patch)
if self.prerelease:
version += '-%s' % self.prerelease
if self.build_metadata:
version += '+' + self.build_metadata
return version
def __repr__(self):
return 'Version.parse(%r)' % str(self)