Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 '''
6 This is a really crummy, slow Python implementation of the Mozilla
7 platform's nsIVersionComparator interface:
9 https://developer.mozilla.org/En/NsIVersionComparator
11 For more information, also see:
13 http://mxr.mozilla.org/mozilla/source/xpcom/glue/nsVersionComparator.cpp
14 '''
16 import re
17 import sys
19 class VersionPart(object):
20 '''
21 Examples:
23 >>> VersionPart('1')
24 (1, None, 0, None)
26 >>> VersionPart('1pre')
27 (1, 'pre', 0, None)
29 >>> VersionPart('1pre10')
30 (1, 'pre', 10, None)
32 >>> VersionPart('1pre10a')
33 (1, 'pre', 10, 'a')
35 >>> VersionPart('1+')
36 (2, 'pre', 0, None)
38 >>> VersionPart('*').numA == sys.maxint
39 True
41 >>> VersionPart('1') < VersionPart('2')
42 True
44 >>> VersionPart('2') > VersionPart('1')
45 True
47 >>> VersionPart('1') == VersionPart('1')
48 True
50 >>> VersionPart('1pre') > VersionPart('1')
51 False
53 >>> VersionPart('1') < VersionPart('1pre')
54 False
56 >>> VersionPart('1pre1') < VersionPart('1pre2')
57 True
59 >>> VersionPart('1pre10b') > VersionPart('1pre10a')
60 True
62 >>> VersionPart('1pre10b') == VersionPart('1pre10b')
63 True
65 >>> VersionPart('1pre10a') < VersionPart('1pre10b')
66 True
68 >>> VersionPart('1') > VersionPart('')
69 True
70 '''
72 _int_part = re.compile('[+-]?(\d*)(.*)')
73 _num_chars = '0123456789+-'
75 def __init__(self, part):
76 self.numA = 0
77 self.strB = None
78 self.numC = 0
79 self.extraD = None
81 if not part:
82 return
84 if part == '*':
85 self.numA = sys.maxint
86 else:
87 match = self._int_part.match(part)
88 self.numA = int(match.group(1))
89 self.strB = match.group(2) or None
90 if self.strB == '+':
91 self.strB = 'pre'
92 self.numA += 1
93 elif self.strB:
94 i = 0
95 num_found = -1
96 for char in self.strB:
97 if char in self._num_chars:
98 num_found = i
99 break
100 i += 1
101 if num_found != -1:
102 match = self._int_part.match(self.strB[num_found:])
103 self.numC = int(match.group(1))
104 self.extraD = match.group(2) or None
105 self.strB = self.strB[:num_found]
107 def _strcmp(self, str1, str2):
108 # Any string is *before* no string.
109 if str1 is None:
110 if str2 is None:
111 return 0
112 else:
113 return 1
115 if str2 is None:
116 return -1
118 return cmp(str1, str2)
120 def __cmp__(self, other):
121 r = cmp(self.numA, other.numA)
122 if r:
123 return r
125 r = self._strcmp(self.strB, other.strB)
126 if r:
127 return r
129 r = cmp(self.numC, other.numC)
130 if r:
131 return r
133 return self._strcmp(self.extraD, other.extraD)
135 def __repr__(self):
136 return repr((self.numA, self.strB, self.numC, self.extraD))
138 def compare(a, b):
139 '''
140 Examples:
142 >>> compare('1', '2')
143 -1
145 >>> compare('1', '1')
146 0
148 >>> compare('2', '1')
149 1
151 >>> compare('1.0pre1', '1.0pre2')
152 -1
154 >>> compare('1.0pre2', '1.0')
155 -1
157 >>> compare('1.0', '1.0.0')
158 0
160 >>> compare('1.0.0', '1.0.0.0')
161 0
163 >>> compare('1.0.0.0', '1.1pre')
164 -1
166 >>> compare('1.1pre', '1.1pre0')
167 0
169 >>> compare('1.1pre0', '1.0+')
170 0
172 >>> compare('1.0+', '1.1pre1a')
173 -1
175 >>> compare('1.1pre1a', '1.1pre1')
176 -1
178 >>> compare('1.1pre1', '1.1pre10a')
179 -1
181 >>> compare('1.1pre10a', '1.1pre10')
182 -1
184 >>> compare('1.1pre10a', '1.*')
185 -1
186 '''
188 a_parts = a.split('.')
189 b_parts = b.split('.')
191 if len(a_parts) < len(b_parts):
192 a_parts.extend([''] * (len(b_parts) - len(a_parts)))
193 else:
194 b_parts.extend([''] * (len(a_parts) - len(b_parts)))
196 for a_part, b_part in zip(a_parts, b_parts):
197 r = cmp(VersionPart(a_part), VersionPart(b_part))
198 if r:
199 return r
201 return 0
203 if __name__ == '__main__':
204 import doctest
206 doctest.testmod(verbose=True)