| 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 |