python (3.11.7)

(root)/
lib/
python3.11/
site-packages/
setuptools/
_distutils/
version.py
       1  #
       2  # distutils/version.py
       3  #
       4  # Implements multiple version numbering conventions for the
       5  # Python Module Distribution Utilities.
       6  #
       7  # $Id$
       8  #
       9  
      10  """Provides classes to represent module version numbers (one class for
      11  each style of version numbering).  There are currently two such classes
      12  implemented: StrictVersion and LooseVersion.
      13  
      14  Every version number class implements the following interface:
      15    * the 'parse' method takes a string and parses it to some internal
      16      representation; if the string is an invalid version number,
      17      'parse' raises a ValueError exception
      18    * the class constructor takes an optional string argument which,
      19      if supplied, is passed to 'parse'
      20    * __str__ reconstructs the string that was passed to 'parse' (or
      21      an equivalent string -- ie. one that will generate an equivalent
      22      version number instance)
      23    * __repr__ generates Python code to recreate the version number instance
      24    * _cmp compares the current instance with either another instance
      25      of the same class or a string (which will be parsed to an instance
      26      of the same class, thus must follow the same rules)
      27  """
      28  
      29  import re
      30  import warnings
      31  import contextlib
      32  
      33  
      34  @contextlib.contextmanager
      35  def suppress_known_deprecation():
      36      with warnings.catch_warnings(record=True) as ctx:
      37          warnings.filterwarnings(
      38              action='default',
      39              category=DeprecationWarning,
      40              message="distutils Version classes are deprecated.",
      41          )
      42          yield ctx
      43  
      44  
      45  class ESC[4;38;5;81mVersion:
      46      """Abstract base class for version numbering classes.  Just provides
      47      constructor (__init__) and reproducer (__repr__), because those
      48      seem to be the same for all version numbering classes; and route
      49      rich comparisons to _cmp.
      50      """
      51  
      52      def __init__(self, vstring=None):
      53          if vstring:
      54              self.parse(vstring)
      55          warnings.warn(
      56              "distutils Version classes are deprecated. "
      57              "Use packaging.version instead.",
      58              DeprecationWarning,
      59              stacklevel=2,
      60          )
      61  
      62      def __repr__(self):
      63          return "{} ('{}')".format(self.__class__.__name__, str(self))
      64  
      65      def __eq__(self, other):
      66          c = self._cmp(other)
      67          if c is NotImplemented:
      68              return c
      69          return c == 0
      70  
      71      def __lt__(self, other):
      72          c = self._cmp(other)
      73          if c is NotImplemented:
      74              return c
      75          return c < 0
      76  
      77      def __le__(self, other):
      78          c = self._cmp(other)
      79          if c is NotImplemented:
      80              return c
      81          return c <= 0
      82  
      83      def __gt__(self, other):
      84          c = self._cmp(other)
      85          if c is NotImplemented:
      86              return c
      87          return c > 0
      88  
      89      def __ge__(self, other):
      90          c = self._cmp(other)
      91          if c is NotImplemented:
      92              return c
      93          return c >= 0
      94  
      95  
      96  # Interface for version-number classes -- must be implemented
      97  # by the following classes (the concrete ones -- Version should
      98  # be treated as an abstract class).
      99  #    __init__ (string) - create and take same action as 'parse'
     100  #                        (string parameter is optional)
     101  #    parse (string)    - convert a string representation to whatever
     102  #                        internal representation is appropriate for
     103  #                        this style of version numbering
     104  #    __str__ (self)    - convert back to a string; should be very similar
     105  #                        (if not identical to) the string supplied to parse
     106  #    __repr__ (self)   - generate Python code to recreate
     107  #                        the instance
     108  #    _cmp (self, other) - compare two version numbers ('other' may
     109  #                        be an unparsed version string, or another
     110  #                        instance of your version class)
     111  
     112  
     113  class ESC[4;38;5;81mStrictVersion(ESC[4;38;5;149mVersion):
     114  
     115      """Version numbering for anal retentives and software idealists.
     116      Implements the standard interface for version number classes as
     117      described above.  A version number consists of two or three
     118      dot-separated numeric components, with an optional "pre-release" tag
     119      on the end.  The pre-release tag consists of the letter 'a' or 'b'
     120      followed by a number.  If the numeric components of two version
     121      numbers are equal, then one with a pre-release tag will always
     122      be deemed earlier (lesser) than one without.
     123  
     124      The following are valid version numbers (shown in the order that
     125      would be obtained by sorting according to the supplied cmp function):
     126  
     127          0.4       0.4.0  (these two are equivalent)
     128          0.4.1
     129          0.5a1
     130          0.5b3
     131          0.5
     132          0.9.6
     133          1.0
     134          1.0.4a3
     135          1.0.4b1
     136          1.0.4
     137  
     138      The following are examples of invalid version numbers:
     139  
     140          1
     141          2.7.2.2
     142          1.3.a4
     143          1.3pl1
     144          1.3c4
     145  
     146      The rationale for this version numbering system will be explained
     147      in the distutils documentation.
     148      """
     149  
     150      version_re = re.compile(
     151          r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$', re.VERBOSE | re.ASCII
     152      )
     153  
     154      def parse(self, vstring):
     155          match = self.version_re.match(vstring)
     156          if not match:
     157              raise ValueError("invalid version number '%s'" % vstring)
     158  
     159          (major, minor, patch, prerelease, prerelease_num) = match.group(1, 2, 4, 5, 6)
     160  
     161          if patch:
     162              self.version = tuple(map(int, [major, minor, patch]))
     163          else:
     164              self.version = tuple(map(int, [major, minor])) + (0,)
     165  
     166          if prerelease:
     167              self.prerelease = (prerelease[0], int(prerelease_num))
     168          else:
     169              self.prerelease = None
     170  
     171      def __str__(self):
     172  
     173          if self.version[2] == 0:
     174              vstring = '.'.join(map(str, self.version[0:2]))
     175          else:
     176              vstring = '.'.join(map(str, self.version))
     177  
     178          if self.prerelease:
     179              vstring = vstring + self.prerelease[0] + str(self.prerelease[1])
     180  
     181          return vstring
     182  
     183      def _cmp(self, other):  # noqa: C901
     184          if isinstance(other, str):
     185              with suppress_known_deprecation():
     186                  other = StrictVersion(other)
     187          elif not isinstance(other, StrictVersion):
     188              return NotImplemented
     189  
     190          if self.version != other.version:
     191              # numeric versions don't match
     192              # prerelease stuff doesn't matter
     193              if self.version < other.version:
     194                  return -1
     195              else:
     196                  return 1
     197  
     198          # have to compare prerelease
     199          # case 1: neither has prerelease; they're equal
     200          # case 2: self has prerelease, other doesn't; other is greater
     201          # case 3: self doesn't have prerelease, other does: self is greater
     202          # case 4: both have prerelease: must compare them!
     203  
     204          if not self.prerelease and not other.prerelease:
     205              return 0
     206          elif self.prerelease and not other.prerelease:
     207              return -1
     208          elif not self.prerelease and other.prerelease:
     209              return 1
     210          elif self.prerelease and other.prerelease:
     211              if self.prerelease == other.prerelease:
     212                  return 0
     213              elif self.prerelease < other.prerelease:
     214                  return -1
     215              else:
     216                  return 1
     217          else:
     218              assert False, "never get here"
     219  
     220  
     221  # end class StrictVersion
     222  
     223  
     224  # The rules according to Greg Stein:
     225  # 1) a version number has 1 or more numbers separated by a period or by
     226  #    sequences of letters. If only periods, then these are compared
     227  #    left-to-right to determine an ordering.
     228  # 2) sequences of letters are part of the tuple for comparison and are
     229  #    compared lexicographically
     230  # 3) recognize the numeric components may have leading zeroes
     231  #
     232  # The LooseVersion class below implements these rules: a version number
     233  # string is split up into a tuple of integer and string components, and
     234  # comparison is a simple tuple comparison.  This means that version
     235  # numbers behave in a predictable and obvious way, but a way that might
     236  # not necessarily be how people *want* version numbers to behave.  There
     237  # wouldn't be a problem if people could stick to purely numeric version
     238  # numbers: just split on period and compare the numbers as tuples.
     239  # However, people insist on putting letters into their version numbers;
     240  # the most common purpose seems to be:
     241  #   - indicating a "pre-release" version
     242  #     ('alpha', 'beta', 'a', 'b', 'pre', 'p')
     243  #   - indicating a post-release patch ('p', 'pl', 'patch')
     244  # but of course this can't cover all version number schemes, and there's
     245  # no way to know what a programmer means without asking him.
     246  #
     247  # The problem is what to do with letters (and other non-numeric
     248  # characters) in a version number.  The current implementation does the
     249  # obvious and predictable thing: keep them as strings and compare
     250  # lexically within a tuple comparison.  This has the desired effect if
     251  # an appended letter sequence implies something "post-release":
     252  # eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002".
     253  #
     254  # However, if letters in a version number imply a pre-release version,
     255  # the "obvious" thing isn't correct.  Eg. you would expect that
     256  # "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison
     257  # implemented here, this just isn't so.
     258  #
     259  # Two possible solutions come to mind.  The first is to tie the
     260  # comparison algorithm to a particular set of semantic rules, as has
     261  # been done in the StrictVersion class above.  This works great as long
     262  # as everyone can go along with bondage and discipline.  Hopefully a
     263  # (large) subset of Python module programmers will agree that the
     264  # particular flavour of bondage and discipline provided by StrictVersion
     265  # provides enough benefit to be worth using, and will submit their
     266  # version numbering scheme to its domination.  The free-thinking
     267  # anarchists in the lot will never give in, though, and something needs
     268  # to be done to accommodate them.
     269  #
     270  # Perhaps a "moderately strict" version class could be implemented that
     271  # lets almost anything slide (syntactically), and makes some heuristic
     272  # assumptions about non-digits in version number strings.  This could
     273  # sink into special-case-hell, though; if I was as talented and
     274  # idiosyncratic as Larry Wall, I'd go ahead and implement a class that
     275  # somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is
     276  # just as happy dealing with things like "2g6" and "1.13++".  I don't
     277  # think I'm smart enough to do it right though.
     278  #
     279  # In any case, I've coded the test suite for this module (see
     280  # ../test/test_version.py) specifically to fail on things like comparing
     281  # "1.2a2" and "1.2".  That's not because the *code* is doing anything
     282  # wrong, it's because the simple, obvious design doesn't match my
     283  # complicated, hairy expectations for real-world version numbers.  It
     284  # would be a snap to fix the test suite to say, "Yep, LooseVersion does
     285  # the Right Thing" (ie. the code matches the conception).  But I'd rather
     286  # have a conception that matches common notions about version numbers.
     287  
     288  
     289  class ESC[4;38;5;81mLooseVersion(ESC[4;38;5;149mVersion):
     290  
     291      """Version numbering for anarchists and software realists.
     292      Implements the standard interface for version number classes as
     293      described above.  A version number consists of a series of numbers,
     294      separated by either periods or strings of letters.  When comparing
     295      version numbers, the numeric components will be compared
     296      numerically, and the alphabetic components lexically.  The following
     297      are all valid version numbers, in no particular order:
     298  
     299          1.5.1
     300          1.5.2b2
     301          161
     302          3.10a
     303          8.02
     304          3.4j
     305          1996.07.12
     306          3.2.pl0
     307          3.1.1.6
     308          2g6
     309          11g
     310          0.960923
     311          2.2beta29
     312          1.13++
     313          5.5.kw
     314          2.0b1pl0
     315  
     316      In fact, there is no such thing as an invalid version number under
     317      this scheme; the rules for comparison are simple and predictable,
     318      but may not always give the results you want (for some definition
     319      of "want").
     320      """
     321  
     322      component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE)
     323  
     324      def parse(self, vstring):
     325          # I've given up on thinking I can reconstruct the version string
     326          # from the parsed tuple -- so I just store the string here for
     327          # use by __str__
     328          self.vstring = vstring
     329          components = [x for x in self.component_re.split(vstring) if x and x != '.']
     330          for i, obj in enumerate(components):
     331              try:
     332                  components[i] = int(obj)
     333              except ValueError:
     334                  pass
     335  
     336          self.version = components
     337  
     338      def __str__(self):
     339          return self.vstring
     340  
     341      def __repr__(self):
     342          return "LooseVersion ('%s')" % str(self)
     343  
     344      def _cmp(self, other):
     345          if isinstance(other, str):
     346              other = LooseVersion(other)
     347          elif not isinstance(other, LooseVersion):
     348              return NotImplemented
     349  
     350          if self.version == other.version:
     351              return 0
     352          if self.version < other.version:
     353              return -1
     354          if self.version > other.version:
     355              return 1
     356  
     357  
     358  # end class LooseVersion