__init__.py 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. Versions for Python packages.
  5. See L{Version}.
  6. """
  7. from __future__ import division, absolute_import
  8. import sys
  9. import warnings
  10. #
  11. # Compat functions
  12. #
  13. if sys.version_info < (3, 0):
  14. _PY3 = False
  15. else:
  16. _PY3 = True
  17. try:
  18. _cmp = cmp
  19. except NameError:
  20. def _cmp(a, b):
  21. """
  22. Compare two objects.
  23. Returns a negative number if C{a < b}, zero if they are equal, and a
  24. positive number if C{a > b}.
  25. """
  26. if a < b:
  27. return -1
  28. elif a == b:
  29. return 0
  30. else:
  31. return 1
  32. def _comparable(klass):
  33. """
  34. Class decorator that ensures support for the special C{__cmp__} method.
  35. On Python 2 this does nothing.
  36. On Python 3, C{__eq__}, C{__lt__}, etc. methods are added to the class,
  37. relying on C{__cmp__} to implement their comparisons.
  38. """
  39. # On Python 2, __cmp__ will just work, so no need to add extra methods:
  40. if not _PY3:
  41. return klass
  42. def __eq__(self, other):
  43. c = self.__cmp__(other)
  44. if c is NotImplemented:
  45. return c
  46. return c == 0
  47. def __ne__(self, other):
  48. c = self.__cmp__(other)
  49. if c is NotImplemented:
  50. return c
  51. return c != 0
  52. def __lt__(self, other):
  53. c = self.__cmp__(other)
  54. if c is NotImplemented:
  55. return c
  56. return c < 0
  57. def __le__(self, other):
  58. c = self.__cmp__(other)
  59. if c is NotImplemented:
  60. return c
  61. return c <= 0
  62. def __gt__(self, other):
  63. c = self.__cmp__(other)
  64. if c is NotImplemented:
  65. return c
  66. return c > 0
  67. def __ge__(self, other):
  68. c = self.__cmp__(other)
  69. if c is NotImplemented:
  70. return c
  71. return c >= 0
  72. klass.__lt__ = __lt__
  73. klass.__gt__ = __gt__
  74. klass.__le__ = __le__
  75. klass.__ge__ = __ge__
  76. klass.__eq__ = __eq__
  77. klass.__ne__ = __ne__
  78. return klass
  79. #
  80. # Versioning
  81. #
  82. @_comparable
  83. class _inf(object):
  84. """
  85. An object that is bigger than all other objects.
  86. """
  87. def __cmp__(self, other):
  88. """
  89. @param other: Another object.
  90. @type other: any
  91. @return: 0 if other is inf, 1 otherwise.
  92. @rtype: C{int}
  93. """
  94. if other is _inf:
  95. return 0
  96. return 1
  97. _inf = _inf()
  98. class IncomparableVersions(TypeError):
  99. """
  100. Two versions could not be compared.
  101. """
  102. @_comparable
  103. class Version(object):
  104. """
  105. An encapsulation of a version for a project, with support for outputting
  106. PEP-440 compatible version strings.
  107. This class supports the standard major.minor.micro[rcN] scheme of
  108. versioning.
  109. """
  110. def __init__(self, package, major, minor, micro, release_candidate=None,
  111. prerelease=None, dev=None):
  112. """
  113. @param package: Name of the package that this is a version of.
  114. @type package: C{str}
  115. @param major: The major version number.
  116. @type major: C{int} or C{str} (for the "NEXT" symbol)
  117. @param minor: The minor version number.
  118. @type minor: C{int}
  119. @param micro: The micro version number.
  120. @type micro: C{int}
  121. @param release_candidate: The release candidate number.
  122. @type release_candidate: C{int}
  123. @param prerelease: The prerelease number. (Deprecated)
  124. @type prerelease: C{int}
  125. @param dev: The development release number.
  126. @type dev: C{int}
  127. """
  128. if release_candidate and prerelease:
  129. raise ValueError("Please only return one of these.")
  130. elif prerelease and not release_candidate:
  131. release_candidate = prerelease
  132. warnings.warn(("Passing prerelease to incremental.Version was "
  133. "deprecated in Incremental 16.9.0. Please pass "
  134. "release_candidate instead."),
  135. DeprecationWarning, stacklevel=2)
  136. if major == "NEXT":
  137. if minor or micro or release_candidate or dev:
  138. raise ValueError(("When using NEXT, all other values except "
  139. "Package must be 0."))
  140. self.package = package
  141. self.major = major
  142. self.minor = minor
  143. self.micro = micro
  144. self.release_candidate = release_candidate
  145. self.dev = dev
  146. @property
  147. def prerelease(self):
  148. warnings.warn(("Accessing incremental.Version.prerelease was "
  149. "deprecated in Incremental 16.9.0. Use "
  150. "Version.release_candidate instead."),
  151. DeprecationWarning, stacklevel=2),
  152. return self.release_candidate
  153. def public(self):
  154. """
  155. Return a PEP440-compatible "public" representation of this L{Version}.
  156. Examples:
  157. - 14.4.0
  158. - 1.2.3rc1
  159. - 14.2.1rc1dev9
  160. - 16.04.0dev0
  161. """
  162. if self.major == "NEXT":
  163. return self.major
  164. if self.release_candidate is None:
  165. rc = ""
  166. else:
  167. rc = "rc%s" % (self.release_candidate,)
  168. if self.dev is None:
  169. dev = ""
  170. else:
  171. dev = "dev%s" % (self.dev,)
  172. return '%r.%d.%d%s%s' % (self.major,
  173. self.minor,
  174. self.micro,
  175. rc, dev)
  176. base = public
  177. short = public
  178. local = public
  179. def __repr__(self):
  180. if self.release_candidate is None:
  181. release_candidate = ""
  182. else:
  183. release_candidate = ", release_candidate=%r" % (
  184. self.release_candidate,)
  185. if self.dev is None:
  186. dev = ""
  187. else:
  188. dev = ", dev=%r" % (self.dev,)
  189. return '%s(%r, %r, %d, %d%s%s)' % (
  190. self.__class__.__name__,
  191. self.package,
  192. self.major,
  193. self.minor,
  194. self.micro,
  195. release_candidate,
  196. dev)
  197. def __str__(self):
  198. return '[%s, version %s]' % (
  199. self.package,
  200. self.short())
  201. def __cmp__(self, other):
  202. """
  203. Compare two versions, considering major versions, minor versions, micro
  204. versions, then release candidates. Package names are case insensitive.
  205. A version with a release candidate is always less than a version
  206. without a release candidate. If both versions have release candidates,
  207. they will be included in the comparison.
  208. @param other: Another version.
  209. @type other: L{Version}
  210. @return: NotImplemented when the other object is not a Version, or one
  211. of -1, 0, or 1.
  212. @raise IncomparableVersions: when the package names of the versions
  213. differ.
  214. """
  215. if not isinstance(other, self.__class__):
  216. return NotImplemented
  217. if self.package.lower() != other.package.lower():
  218. raise IncomparableVersions("%r != %r"
  219. % (self.package, other.package))
  220. if self.major == "NEXT":
  221. major = _inf
  222. else:
  223. major = self.major
  224. if self.release_candidate is None:
  225. release_candidate = _inf
  226. else:
  227. release_candidate = self.release_candidate
  228. if self.dev is None:
  229. dev = _inf
  230. else:
  231. dev = self.dev
  232. if other.major == "NEXT":
  233. othermajor = _inf
  234. else:
  235. othermajor = other.major
  236. if other.release_candidate is None:
  237. otherrc = _inf
  238. else:
  239. otherrc = other.release_candidate
  240. if other.dev is None:
  241. otherdev = _inf
  242. else:
  243. otherdev = other.dev
  244. x = _cmp((major,
  245. self.minor,
  246. self.micro,
  247. release_candidate,
  248. dev),
  249. (othermajor,
  250. other.minor,
  251. other.micro,
  252. otherrc,
  253. otherdev))
  254. return x
  255. def getVersionString(version):
  256. """
  257. Get a friendly string for the given version object.
  258. @param version: A L{Version} object.
  259. @return: A string containing the package and short version number.
  260. """
  261. result = '%s %s' % (version.package, version.short())
  262. return result
  263. def _get_version(dist, keyword, value):
  264. """
  265. Get the version from the package listed in the Distribution.
  266. """
  267. if not value:
  268. return
  269. from distutils.command import build_py
  270. sp_command = build_py.build_py(dist)
  271. sp_command.finalize_options()
  272. for item in sp_command.find_all_modules():
  273. if item[1] == "_version":
  274. version_file = {}
  275. with open(item[2]) as f:
  276. exec(f.read(), version_file)
  277. dist.metadata.version = version_file["__version__"].public()
  278. return None
  279. raise Exception("No _version.py found.")
  280. from ._version import __version__ # noqa
  281. __all__ = ["__version__", "Version", "getVersionString"]