version.py revision 36
3N/A#!/usr/bin/python
3N/A#
3N/A# CDDL HEADER START
3N/A#
3N/A# The contents of this file are subject to the terms of the
3N/A# Common Development and Distribution License (the "License").
3N/A# You may not use this file except in compliance with the License.
3N/A#
3N/A# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
3N/A# or http://www.opensolaris.org/os/licensing.
3N/A# See the License for the specific language governing permissions
3N/A# and limitations under the License.
3N/A#
3N/A# When distributing Covered Code, include this CDDL HEADER in each
3N/A# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
3N/A# If applicable, add the following below this CDDL HEADER, with the
3N/A# fields enclosed by brackets "[]" replaced with your own identifying
3N/A# information: Portions Copyright [yyyy] [name of copyright owner]
3N/A#
3N/A# CDDL HEADER END
3N/A#
3N/A
3N/A#
3N/A# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
3N/A# Use is subject to license terms.
3N/A#
3N/A
3N/Aimport exceptions
3N/Aimport re
3N/Aimport string
3N/A
30N/ACONSTRAINT_NONE = 0
30N/A
30N/ACONSTRAINT_RELEASE = 100
30N/ACONSTRAINT_RELEASE_MAJOR = 101
30N/ACONSTRAINT_RELEASE_MINOR = 102
30N/A
30N/ACONSTRAINT_BRANCH = 200
30N/ACONSTRAINT_BRANCH_MAJOR = 101
30N/ACONSTRAINT_BRANCH_MINOR = 102
30N/A
30N/ACONSTRAINT_SEQUENCE = 300
30N/A
3N/Aclass IllegalDotSequence(exceptions.Exception):
3N/A def __init__(self, args=None):
3N/A self.args = args
3N/A
3N/Aclass DotSequence(object):
3N/A """A DotSequence is the typical "x.y.z" string used in software
3N/A versioning. We define the "major release" value and the "minor release"
3N/A value as the first two numbers in the sequence."""
3N/A
3N/A def __init__(self, dotstring):
3N/A m = re.match("\d+(\.\d)*", dotstring)
3N/A if m == None:
3N/A raise IllegalDotSequence
3N/A self.sequence = map(int, re.split("\.", dotstring))
3N/A
3N/A def __str__(self):
3N/A return string.join(map(str, self.sequence), ".")
3N/A
3N/A def __ne__(self, other):
3N/A if self.sequence != other.sequence:
3N/A return True
3N/A return False
3N/A
3N/A def __eq__(self, other):
3N/A if self.sequence == other.sequence:
3N/A return True
3N/A return False
3N/A
3N/A def __lt__(self, other):
3N/A if self.sequence < other.sequence:
3N/A return True
3N/A return False
3N/A
3N/A def __gt__(self, other):
3N/A if self.sequence > other.sequence:
3N/A return True
3N/A return False
3N/A
30N/A def is_subsequence(self, other):
30N/A """Return true if self is a "subsequence" of other, meaning that
30N/A other and self have identical components, up to the length of
30N/A self's sequence."""
30N/A
30N/A if len(self.sequence) > len(other.sequence):
30N/A return False
30N/A
30N/A for n in xrange(len(self.sequence) - 1):
30N/A if self.sequence[n] != other.sequence[n]:
30N/A return False
30N/A
30N/A return True
30N/A
3N/A def is_same_major(self, other):
3N/A if self.sequence[0] == other.sequence[0]:
3N/A return True
3N/A return False
3N/A
3N/A def is_same_minor(self, other):
3N/A if not is_same_major(self, other):
3N/A return False
3N/A
3N/A if self.sequence[1] == other.sequence[1]:
3N/A return True
3N/A return False
3N/A
3N/Aclass IllegalVersion(exceptions.Exception):
3N/A def __init__(self, args=None):
3N/A self.args = args
3N/A
3N/Aclass Version(object):
32N/A """Version format is release[,build_release]-branch:timestamp, which we
32N/A decompose into three DotSequences and the timestamp. The
22N/A release and branch DotSequences are interpreted normally, where v1 < v2
22N/A implies that v2 is a later release or branch. The build_release
22N/A DotSequence records the system on which the package binaries were
22N/A constructed. Interpretation of the build_release by the client is that,
22N/A in the case b1 < b2, a b1 package can be run on either b1 or b2
22N/A systems,while a b2 package can only be run on a b2 system."""
3N/A
22N/A def __init__(self, version_string, build_string):
3N/A # XXX If illegally formatted, raise exception.
26N/A m = re.match("(\d+[\.\d]*),(\d+[\.\d]*)-(\d+[\.\d]*)\:(\d+)",
22N/A version_string)
3N/A if m != None:
3N/A self.release = DotSequence(m.group(1))
22N/A self.build_release = DotSequence(m.group(2))
22N/A self.branch = DotSequence(m.group(3))
32N/A self.timestamp = m.group(4)
26N/A return
22N/A
26N/A assert build_string != None
26N/A self.build_release = DotSequence(build_string)
26N/A
26N/A m = re.match("(\d+[\.\d]*)-(\d+[\.\d]*)\:(\d+)", version_string)
22N/A if m != None:
22N/A self.release = DotSequence(m.group(1))
22N/A self.branch = DotSequence(m.group(2))
32N/A self.timestamp = int(m.group(3))
3N/A return
3N/A
3N/A # Sequence omitted?
22N/A m = re.match("(\d[\.\d]*)-(\d[\.\d]*)", version_string)
3N/A if m != None:
3N/A self.release = DotSequence(m.group(1))
22N/A self.branch = DotSequence(m.group(2))
32N/A self.timestamp = 0
3N/A return
3N/A
3N/A # Branch omitted?
22N/A m = re.match("(\d[\.\d]*)", version_string)
3N/A if m != None:
3N/A self.release = DotSequence(m.group(1))
22N/A self.branch = DotSequence("0")
32N/A self.timestamp = 0
3N/A return
3N/A
3N/A raise IllegalVersion
3N/A
22N/A def compatible_with_build(self, target):
22N/A """target is a DotSequence for the target system."""
22N/A if self.build_release < target:
22N/A return True
22N/A return False
22N/A
3N/A def __str__(self):
22N/A return "%s,%s-%s:%s" % (self.release, self.build_release,
32N/A self.branch, self.timestamp)
3N/A
23N/A def get_short_version(self):
23N/A return "%s-%s" % (self.release, self.branch)
23N/A
32N/A def set_timestamp(self, new_ts):
32N/A assert new_ts > self.timestamp
32N/A self.timestamp = new_ts
32N/A
32N/A def get_timestamp(self):
32N/A return self.timestamp
32N/A
3N/A def __ne__(self, other):
22N/A if other == None:
22N/A return True
22N/A
3N/A if self.release == other.release and \
3N/A self.branch == other.branch and \
32N/A self.timestamp == other.timestamp:
3N/A return False
3N/A return True
3N/A
3N/A def __eq__(self, other):
22N/A if other == None:
22N/A return False
22N/A
3N/A if self.release == other.release and \
3N/A self.branch == other.branch and \
32N/A self.timestamp == other.timestamp:
3N/A return True
3N/A return False
3N/A
3N/A def __lt__(self, other):
3N/A if self.release < other.release:
3N/A return True
3N/A if self.release != other.release:
3N/A return False
3N/A if self.branch < other.branch:
3N/A return True
3N/A if self.branch != other.branch:
3N/A return False
32N/A if self.timestamp < other.timestamp:
3N/A return True
3N/A return False
3N/A
3N/A def __gt__(self, other):
3N/A if self.release > other.release:
3N/A return True
3N/A if self.release != other.release:
3N/A return False
3N/A if self.branch > other.branch:
3N/A return True
3N/A if self.branch != other.branch:
3N/A return False
32N/A if self.timestamp > other.timestamp:
3N/A return True
3N/A return False
3N/A
24N/A def __cmp__(self, other):
24N/A if self < other:
24N/A return -1
24N/A if self > other:
24N/A return 1
24N/A if self.build_release < other.build_release:
24N/A return -1
24N/A if self.build_release > other.build_release:
24N/A return 1
24N/A return 0
24N/A
30N/A def is_successor(self, other, constraint):
30N/A """Evaluate true if self is a successor version to other.
30N/A
30N/A The loosest constraint is CONSTRAINT_NONE (None is treated
30N/A equivalently, which is a simple test for self > other. As we
30N/A proceed through the policies we get stricter, depending on the
30N/A selected constraint.
30N/A
30N/A For CONSTRAINT_RELEASE, self is a successor to other if all of
30N/A the components of other's release match, and there are later
32N/A components of self's version. The branch and timestamp
30N/A components are ignored.
30N/A
30N/A For CONSTRAINT_RELEASE_MAJOR and CONSTRAINT_RELEASE_MINOR, other
30N/A is effectively truncated to [other[0]] and [other[0], other[1]]
30N/A prior to being treated as for CONSTRAINT_RELEASE.
30N/A
30N/A Similarly for CONSTRAINT_BRANCH, the release fields of other and
30N/A self are expected to be identical, and then the branches are
30N/A compared as releases were for the CONSTRAINT_RELEASE* policies.
30N/A """
30N/A
30N/A if constraint == None or constraint == CONSTRAINT_NONE:
30N/A return self > other
30N/A
30N/A if constraint == CONSTRAINT_RELEASE:
36N/A return other.release.is_subsequence(self.release)
30N/A
30N/A if constraint == CONSTRAINT_RELEASE_MAJOR:
30N/A return other.release.is_same_major(self.release)
30N/A
30N/A if constraint == CONSTRAINT_RELEASE_MINOR:
30N/A return other.release.is_same_minor(self.release)
30N/A
30N/A if constraint == CONSTRAINT_BRANCH:
30N/A return other.branch.is_subsequence(self.branch)
30N/A
30N/A if constraint == CONSTRAINT_BRANCH_MAJOR:
30N/A return other.branch.is_same_major(self.branch)
30N/A
30N/A if constraint == CONSTRAINT_BRANCH_MINOR:
30N/A return other.branch.is_same_minor(self.branch)
30N/A
30N/A raise ValueError, "constraint has unknown value"
30N/A
3N/Aif __name__ == "__main__":
3N/A d1 = DotSequence("1.1.3")
3N/A d2 = DotSequence("1.1.3")
3N/A assert d1 == d2
3N/A
22N/A v1 = Version("5.5.1-10:6", "5.5.1")
22N/A v2 = Version("5.5.1-10:8", "5.5.1")
22N/A v3 = Version("5.5.1-10", "5.5")
22N/A v4 = Version("5.5.1-6", "5.4")
22N/A v5 = Version("5.6,1", "5.4")
22N/A v6 = Version("5.7", "5.4")
22N/A v7 = Version("5.10", "5.5.1")
22N/A v8 = Version("5.10.1", "5.5.1")
22N/A v9 = Version("5.11", "5.5.1")
22N/A
22N/A d3 = DotSequence("5.4")
22N/A d4 = DotSequence("5.6")
3N/A
3N/A assert v1 < v2
3N/A assert v4 < v3
3N/A assert v4 < v5
3N/A assert v6 > v5
3N/A assert v7 < v8
3N/A assert v9 > v8
3N/A assert not v9 == v8
3N/A assert v9 != v8
22N/A
22N/A assert not v9.compatible_with_build(d3)
22N/A assert v9.compatible_with_build(d4)
30N/A
30N/A assert v2.is_successor(v1, CONSTRAINT_BRANCH)
30N/A assert v4.is_successor(v2, CONSTRAINT_RELEASE)
30N/A assert v6.is_successor(v5, CONSTRAINT_RELEASE_MAJOR)
30N/A assert v8.is_successor(v7, CONSTRAINT_RELEASE_MAJOR)
30N/A