versions.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. # Copyright 2012 Matt Chaput. All rights reserved.
  2. #
  3. # Redistribution and use in source and binary forms, with or without
  4. # modification, are permitted provided that the following conditions are met:
  5. #
  6. # 1. Redistributions of source code must retain the above copyright notice,
  7. # this list of conditions and the following disclaimer.
  8. #
  9. # 2. Redistributions in binary form must reproduce the above copyright
  10. # notice, this list of conditions and the following disclaimer in the
  11. # documentation and/or other materials provided with the distribution.
  12. #
  13. # THIS SOFTWARE IS PROVIDED BY MATT CHAPUT ``AS IS'' AND ANY EXPRESS OR
  14. # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  15. # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
  16. # EVENT SHALL MATT CHAPUT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  17. # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  18. # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
  19. # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  20. # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  21. # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  22. # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  23. #
  24. # The views and conclusions contained in the software and documentation are
  25. # those of the authors and should not be interpreted as representing official
  26. # policies, either expressed or implied, of Matt Chaput.
  27. from whoosh.util.text import rcompile
  28. class BaseVersion(object):
  29. @classmethod
  30. def parse(cls, text):
  31. obj = cls()
  32. match = cls._version_exp.match(text)
  33. if match:
  34. groupdict = match.groupdict()
  35. for groupname, typ in cls._parts:
  36. v = groupdict.get(groupname)
  37. if v is not None:
  38. setattr(obj, groupname, typ(v))
  39. return obj
  40. def __repr__(self):
  41. vs = ", ".join(repr(getattr(self, slot)) for slot in self.__slots__)
  42. return "%s(%s)" % (self.__class__.__name__, vs)
  43. def tuple(self):
  44. return tuple(getattr(self, slot) for slot in self.__slots__)
  45. def __eq__(self, other):
  46. if not hasattr(other, "tuple"):
  47. raise ValueError("Can't compare %r with %r" % (self, other))
  48. return self.tuple() == other.tuple()
  49. def __lt__(self, other):
  50. if not hasattr(other, "tuple"):
  51. raise ValueError("Can't compare %r with %r" % (self, other))
  52. return self.tuple() < other.tuple()
  53. # It's dumb that you have to define these
  54. def __gt__(self, other):
  55. if not hasattr(other, "tuple"):
  56. raise ValueError("Can't compare %r with %r" % (self, other))
  57. return self.tuple() > other.tuple()
  58. def __ge__(self, other):
  59. if not hasattr(other, "tuple"):
  60. raise ValueError("Can't compare %r with %r" % (self, other))
  61. return self.tuple() >= other.tuple()
  62. def __le__(self, other):
  63. if not hasattr(other, "tuple"):
  64. raise ValueError("Can't compare %r with %r" % (self, other))
  65. return self.tuple() <= other.tuple()
  66. def __ne__(self, other):
  67. if not hasattr(other, "tuple"):
  68. raise ValueError("Can't compare %r with %r" % (self, other))
  69. return self.tuple() != other.tuple()
  70. class SimpleVersion(BaseVersion):
  71. """An object that parses version numbers such as::
  72. 12.2.5b
  73. The filter supports a limited subset of PEP 386 versions including::
  74. 1
  75. 1.2
  76. 1.2c
  77. 1.2c3
  78. 1.2.3
  79. 1.2.3a
  80. 1.2.3b4
  81. 10.7.5rc1
  82. 999.999.999c999
  83. """
  84. _version_exp = rcompile(r"""
  85. ^
  86. (?P<major>\d{1,4})
  87. (
  88. [.](?P<minor>\d{1,4})
  89. (
  90. [.](?P<release>\d{1,4})
  91. )?
  92. (
  93. (?P<ex>[abc]|rc)
  94. (?P<exnum>\d{1,4})?
  95. )?
  96. )?
  97. $
  98. """, verbose=True)
  99. # (groupid, method, skippable, default)
  100. _parts = [("major", int),
  101. ("minor", int),
  102. ("release", int),
  103. ("ex", str),
  104. ("exnum", int),
  105. ]
  106. _ex_bits = {"a": 0, "b": 1, "c": 2, "rc": 10, "z": 15}
  107. _bits_ex = dict((v, k) for k, v in _ex_bits.items())
  108. __slots__ = ("major", "minor", "release", "ex", "exnum")
  109. def __init__(self, major=1, minor=0, release=0, ex="z", exnum=0):
  110. self.major = major
  111. self.minor = minor
  112. self.release = release
  113. self.ex = ex
  114. self.exnum = exnum
  115. def to_int(self):
  116. assert self.major < 1024
  117. n = self.major << 34
  118. assert self.minor < 1024
  119. n |= self.minor << 24
  120. assert self.release < 1024
  121. n |= self.release << 14
  122. exbits = self._ex_bits.get(self.ex, 15)
  123. n |= exbits << 10
  124. assert self.exnum < 1024
  125. n |= self.exnum
  126. return n
  127. @classmethod
  128. def from_int(cls, n):
  129. major = (n & (1023 << 34)) >> 34
  130. minor = (n & (1023 << 24)) >> 24
  131. release = (n & (1023 << 14)) >> 14
  132. exbits = (n & (7 << 10)) >> 10
  133. ex = cls._bits_ex.get(exbits, "z")
  134. exnum = n & 1023
  135. return cls(major, minor, release, ex, exnum)