OLD | NEW |
| (Empty) |
1 # -*- test-case-name: twisted.python.test.test_versions -*- | |
2 # Copyright (c) 2006-2008 Twisted Matrix Laboratories. | |
3 # See LICENSE for details. | |
4 | |
5 """ | |
6 Versions for Python packages. | |
7 | |
8 See L{Version}. | |
9 """ | |
10 | |
11 import sys, os | |
12 | |
13 | |
14 class _inf(object): | |
15 """ | |
16 An object that is bigger than all other objects. | |
17 """ | |
18 def __cmp__(self, other): | |
19 """ | |
20 @param other: Another object. | |
21 @type other: any | |
22 | |
23 @return: 0 if other is inf, 1 otherwise. | |
24 @rtype: C{int} | |
25 """ | |
26 if other is _inf: | |
27 return 0 | |
28 return 1 | |
29 | |
30 _inf = _inf() | |
31 | |
32 | |
33 class IncomparableVersions(TypeError): | |
34 """ | |
35 Two versions could not be compared. | |
36 """ | |
37 | |
38 class Version(object): | |
39 """ | |
40 An object that represents a three-part version number. | |
41 | |
42 If running from an svn checkout, include the revision number in | |
43 the version string. | |
44 """ | |
45 def __init__(self, package, major, minor, micro, prerelease=None): | |
46 """ | |
47 @param package: Name of the package that this is a version of. | |
48 @type package: C{str} | |
49 @param major: The major version number. | |
50 @type major: C{int} | |
51 @param minor: The minor version number. | |
52 @type minor: C{int} | |
53 @param micro: The micro version number. | |
54 @type micro: C{int} | |
55 @param prerelease: The prerelease number. | |
56 @type prerelease: C{int} | |
57 """ | |
58 self.package = package | |
59 self.major = major | |
60 self.minor = minor | |
61 self.micro = micro | |
62 self.prerelease = prerelease | |
63 | |
64 | |
65 def short(self): | |
66 """ | |
67 Return a string in canonical short version format, | |
68 <major>.<minor>.<micro>[+rSVNVer]. | |
69 """ | |
70 s = self.base() | |
71 svnver = self._getSVNVersion() | |
72 if svnver: | |
73 s += '+r' + str(svnver) | |
74 return s | |
75 | |
76 | |
77 def base(self): | |
78 """ | |
79 Like L{short}, but without the +rSVNVer. | |
80 """ | |
81 if self.prerelease is None: | |
82 pre = "" | |
83 else: | |
84 pre = "pre%s" % (self.prerelease,) | |
85 return '%d.%d.%d%s' % (self.major, | |
86 self.minor, | |
87 self.micro, | |
88 pre) | |
89 | |
90 | |
91 def __repr__(self): | |
92 svnver = self._formatSVNVersion() | |
93 if svnver: | |
94 svnver = ' #' + svnver | |
95 if self.prerelease is None: | |
96 prerelease = "" | |
97 else: | |
98 prerelease = ", prerelease=%r" % (self.prerelease,) | |
99 return '%s(%r, %d, %d, %d%s)%s' % ( | |
100 self.__class__.__name__, | |
101 self.package, | |
102 self.major, | |
103 self.minor, | |
104 self.micro, | |
105 prerelease, | |
106 svnver) | |
107 | |
108 | |
109 def __str__(self): | |
110 return '[%s, version %s]' % ( | |
111 self.package, | |
112 self.short()) | |
113 | |
114 | |
115 def __cmp__(self, other): | |
116 """ | |
117 Compare two versions, considering major versions, minor versions, micro | |
118 versions, then prereleases. | |
119 | |
120 A version with a prerelease is always less than a version without a | |
121 prerelease. If both versions have prereleases, they will be included in | |
122 the comparison. | |
123 | |
124 @param other: Another version. | |
125 @type other: L{Version} | |
126 | |
127 @return: NotImplemented when the other object is not a Version, or one | |
128 of -1, 0, or 1. | |
129 | |
130 @raise IncomparableVersions: when the package names of the versions | |
131 differ. | |
132 """ | |
133 if not isinstance(other, self.__class__): | |
134 return NotImplemented | |
135 if self.package != other.package: | |
136 raise IncomparableVersions("%r != %r" | |
137 % (self.package, other.package)) | |
138 | |
139 if self.prerelease is None: | |
140 prerelease = _inf | |
141 else: | |
142 prerelease = self.prerelease | |
143 | |
144 if other.prerelease is None: | |
145 otherpre = _inf | |
146 else: | |
147 otherpre = other.prerelease | |
148 | |
149 x = cmp((self.major, | |
150 self.minor, | |
151 self.micro, | |
152 prerelease), | |
153 (other.major, | |
154 other.minor, | |
155 other.micro, | |
156 otherpre)) | |
157 return x | |
158 | |
159 | |
160 def _parseSVNEntries_4(self, entriesFile): | |
161 """ | |
162 Given a readable file object which represents a .svn/entries file in | |
163 format version 4, return the revision as a string. We do this by | |
164 reading first XML element in the document that has a 'revision' | |
165 attribute. | |
166 """ | |
167 from xml.dom.minidom import parse | |
168 doc = parse(entriesFile).documentElement | |
169 for node in doc.childNodes: | |
170 if hasattr(node, 'getAttribute'): | |
171 rev = node.getAttribute('revision') | |
172 if rev is not None: | |
173 return rev.encode('ascii') | |
174 | |
175 | |
176 def _parseSVNEntries_8(self, entriesFile): | |
177 """ | |
178 Given a readable file object which represents a .svn/entries file in | |
179 format version 8, return the revision as a string. | |
180 """ | |
181 entriesFile.readline() | |
182 entriesFile.readline() | |
183 entriesFile.readline() | |
184 return entriesFile.readline().strip() | |
185 | |
186 | |
187 def _getSVNVersion(self): | |
188 """ | |
189 Figure out the SVN revision number based on the existance of | |
190 <package>/.svn/entries, and its contents. This requires discovering the | |
191 format version from the 'format' file and parsing the entries file | |
192 accordingly. | |
193 | |
194 @return: None or string containing SVN Revision number. | |
195 """ | |
196 mod = sys.modules.get(self.package) | |
197 if mod: | |
198 svn = os.path.join(os.path.dirname(mod.__file__), '.svn') | |
199 formatFile = os.path.join(svn, 'format') | |
200 if not os.path.exists(formatFile): | |
201 return None | |
202 format = file(formatFile).read().strip() | |
203 ent = os.path.join(svn, 'entries') | |
204 if not os.path.exists(ent): | |
205 return None | |
206 parser = getattr(self, '_parseSVNEntries_' + format, None) | |
207 if parser is None: | |
208 return 'Unknown' | |
209 entries = file(ent) | |
210 try: | |
211 try: | |
212 return parser(entries) | |
213 finally: | |
214 entries.close() | |
215 except: | |
216 return 'Unknown' | |
217 | |
218 | |
219 def _formatSVNVersion(self): | |
220 ver = self._getSVNVersion() | |
221 if ver is None: | |
222 return '' | |
223 return ' (SVN r%s)' % (ver,) | |
224 | |
225 | |
226 | |
227 def getVersionString(version): | |
228 """ | |
229 Get a friendly string for the given version object. | |
230 | |
231 @param version: A L{Version} object. | |
232 @return: A string containing the package and short version number. | |
233 """ | |
234 result = '%s %s' % (version.package, version.short()) | |
235 return result | |
OLD | NEW |