1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/addon-sdk/source/python-lib/cuddlefish/version_comparator.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,206 @@ 1.4 +# This Source Code Form is subject to the terms of the Mozilla Public 1.5 +# License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 +# file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.7 + 1.8 +''' 1.9 + This is a really crummy, slow Python implementation of the Mozilla 1.10 + platform's nsIVersionComparator interface: 1.11 + 1.12 + https://developer.mozilla.org/En/NsIVersionComparator 1.13 + 1.14 + For more information, also see: 1.15 + 1.16 + http://mxr.mozilla.org/mozilla/source/xpcom/glue/nsVersionComparator.cpp 1.17 +''' 1.18 + 1.19 +import re 1.20 +import sys 1.21 + 1.22 +class VersionPart(object): 1.23 + ''' 1.24 + Examples: 1.25 + 1.26 + >>> VersionPart('1') 1.27 + (1, None, 0, None) 1.28 + 1.29 + >>> VersionPart('1pre') 1.30 + (1, 'pre', 0, None) 1.31 + 1.32 + >>> VersionPart('1pre10') 1.33 + (1, 'pre', 10, None) 1.34 + 1.35 + >>> VersionPart('1pre10a') 1.36 + (1, 'pre', 10, 'a') 1.37 + 1.38 + >>> VersionPart('1+') 1.39 + (2, 'pre', 0, None) 1.40 + 1.41 + >>> VersionPart('*').numA == sys.maxint 1.42 + True 1.43 + 1.44 + >>> VersionPart('1') < VersionPart('2') 1.45 + True 1.46 + 1.47 + >>> VersionPart('2') > VersionPart('1') 1.48 + True 1.49 + 1.50 + >>> VersionPart('1') == VersionPart('1') 1.51 + True 1.52 + 1.53 + >>> VersionPart('1pre') > VersionPart('1') 1.54 + False 1.55 + 1.56 + >>> VersionPart('1') < VersionPart('1pre') 1.57 + False 1.58 + 1.59 + >>> VersionPart('1pre1') < VersionPart('1pre2') 1.60 + True 1.61 + 1.62 + >>> VersionPart('1pre10b') > VersionPart('1pre10a') 1.63 + True 1.64 + 1.65 + >>> VersionPart('1pre10b') == VersionPart('1pre10b') 1.66 + True 1.67 + 1.68 + >>> VersionPart('1pre10a') < VersionPart('1pre10b') 1.69 + True 1.70 + 1.71 + >>> VersionPart('1') > VersionPart('') 1.72 + True 1.73 + ''' 1.74 + 1.75 + _int_part = re.compile('[+-]?(\d*)(.*)') 1.76 + _num_chars = '0123456789+-' 1.77 + 1.78 + def __init__(self, part): 1.79 + self.numA = 0 1.80 + self.strB = None 1.81 + self.numC = 0 1.82 + self.extraD = None 1.83 + 1.84 + if not part: 1.85 + return 1.86 + 1.87 + if part == '*': 1.88 + self.numA = sys.maxint 1.89 + else: 1.90 + match = self._int_part.match(part) 1.91 + self.numA = int(match.group(1)) 1.92 + self.strB = match.group(2) or None 1.93 + if self.strB == '+': 1.94 + self.strB = 'pre' 1.95 + self.numA += 1 1.96 + elif self.strB: 1.97 + i = 0 1.98 + num_found = -1 1.99 + for char in self.strB: 1.100 + if char in self._num_chars: 1.101 + num_found = i 1.102 + break 1.103 + i += 1 1.104 + if num_found != -1: 1.105 + match = self._int_part.match(self.strB[num_found:]) 1.106 + self.numC = int(match.group(1)) 1.107 + self.extraD = match.group(2) or None 1.108 + self.strB = self.strB[:num_found] 1.109 + 1.110 + def _strcmp(self, str1, str2): 1.111 + # Any string is *before* no string. 1.112 + if str1 is None: 1.113 + if str2 is None: 1.114 + return 0 1.115 + else: 1.116 + return 1 1.117 + 1.118 + if str2 is None: 1.119 + return -1 1.120 + 1.121 + return cmp(str1, str2) 1.122 + 1.123 + def __cmp__(self, other): 1.124 + r = cmp(self.numA, other.numA) 1.125 + if r: 1.126 + return r 1.127 + 1.128 + r = self._strcmp(self.strB, other.strB) 1.129 + if r: 1.130 + return r 1.131 + 1.132 + r = cmp(self.numC, other.numC) 1.133 + if r: 1.134 + return r 1.135 + 1.136 + return self._strcmp(self.extraD, other.extraD) 1.137 + 1.138 + def __repr__(self): 1.139 + return repr((self.numA, self.strB, self.numC, self.extraD)) 1.140 + 1.141 +def compare(a, b): 1.142 + ''' 1.143 + Examples: 1.144 + 1.145 + >>> compare('1', '2') 1.146 + -1 1.147 + 1.148 + >>> compare('1', '1') 1.149 + 0 1.150 + 1.151 + >>> compare('2', '1') 1.152 + 1 1.153 + 1.154 + >>> compare('1.0pre1', '1.0pre2') 1.155 + -1 1.156 + 1.157 + >>> compare('1.0pre2', '1.0') 1.158 + -1 1.159 + 1.160 + >>> compare('1.0', '1.0.0') 1.161 + 0 1.162 + 1.163 + >>> compare('1.0.0', '1.0.0.0') 1.164 + 0 1.165 + 1.166 + >>> compare('1.0.0.0', '1.1pre') 1.167 + -1 1.168 + 1.169 + >>> compare('1.1pre', '1.1pre0') 1.170 + 0 1.171 + 1.172 + >>> compare('1.1pre0', '1.0+') 1.173 + 0 1.174 + 1.175 + >>> compare('1.0+', '1.1pre1a') 1.176 + -1 1.177 + 1.178 + >>> compare('1.1pre1a', '1.1pre1') 1.179 + -1 1.180 + 1.181 + >>> compare('1.1pre1', '1.1pre10a') 1.182 + -1 1.183 + 1.184 + >>> compare('1.1pre10a', '1.1pre10') 1.185 + -1 1.186 + 1.187 + >>> compare('1.1pre10a', '1.*') 1.188 + -1 1.189 + ''' 1.190 + 1.191 + a_parts = a.split('.') 1.192 + b_parts = b.split('.') 1.193 + 1.194 + if len(a_parts) < len(b_parts): 1.195 + a_parts.extend([''] * (len(b_parts) - len(a_parts))) 1.196 + else: 1.197 + b_parts.extend([''] * (len(a_parts) - len(b_parts))) 1.198 + 1.199 + for a_part, b_part in zip(a_parts, b_parts): 1.200 + r = cmp(VersionPart(a_part), VersionPart(b_part)) 1.201 + if r: 1.202 + return r 1.203 + 1.204 + return 0 1.205 + 1.206 +if __name__ == '__main__': 1.207 + import doctest 1.208 + 1.209 + doctest.testmod(verbose=True)