Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
keewenaw
GitHub Repository: keewenaw/ethereum-wallet-cracker
Path: blob/main/test/lib/python3.9/site-packages/setuptools/_distutils/version.py
4799 views
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 Version:
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 "%s ('%s')" % (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 StrictVersion (Version):
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(r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$',
151
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) = \
160
match.group(1, 2, 4, 5, 6)
161
162
if patch:
163
self.version = tuple(map(int, [major, minor, patch]))
164
else:
165
self.version = tuple(map(int, [major, minor])) + (0,)
166
167
if prerelease:
168
self.prerelease = (prerelease[0], int(prerelease_num))
169
else:
170
self.prerelease = None
171
172
173
def __str__ (self):
174
175
if self.version[2] == 0:
176
vstring = '.'.join(map(str, self.version[0:2]))
177
else:
178
vstring = '.'.join(map(str, self.version))
179
180
if self.prerelease:
181
vstring = vstring + self.prerelease[0] + str(self.prerelease[1])
182
183
return vstring
184
185
186
def _cmp (self, other):
187
if isinstance(other, str):
188
with suppress_known_deprecation():
189
other = StrictVersion(other)
190
elif not isinstance(other, StrictVersion):
191
return NotImplemented
192
193
if self.version != other.version:
194
# numeric versions don't match
195
# prerelease stuff doesn't matter
196
if self.version < other.version:
197
return -1
198
else:
199
return 1
200
201
# have to compare prerelease
202
# case 1: neither has prerelease; they're equal
203
# case 2: self has prerelease, other doesn't; other is greater
204
# case 3: self doesn't have prerelease, other does: self is greater
205
# case 4: both have prerelease: must compare them!
206
207
if (not self.prerelease and not other.prerelease):
208
return 0
209
elif (self.prerelease and not other.prerelease):
210
return -1
211
elif (not self.prerelease and other.prerelease):
212
return 1
213
elif (self.prerelease and other.prerelease):
214
if self.prerelease == other.prerelease:
215
return 0
216
elif self.prerelease < other.prerelease:
217
return -1
218
else:
219
return 1
220
else:
221
assert False, "never get here"
222
223
# end class StrictVersion
224
225
226
# The rules according to Greg Stein:
227
# 1) a version number has 1 or more numbers separated by a period or by
228
# sequences of letters. If only periods, then these are compared
229
# left-to-right to determine an ordering.
230
# 2) sequences of letters are part of the tuple for comparison and are
231
# compared lexicographically
232
# 3) recognize the numeric components may have leading zeroes
233
#
234
# The LooseVersion class below implements these rules: a version number
235
# string is split up into a tuple of integer and string components, and
236
# comparison is a simple tuple comparison. This means that version
237
# numbers behave in a predictable and obvious way, but a way that might
238
# not necessarily be how people *want* version numbers to behave. There
239
# wouldn't be a problem if people could stick to purely numeric version
240
# numbers: just split on period and compare the numbers as tuples.
241
# However, people insist on putting letters into their version numbers;
242
# the most common purpose seems to be:
243
# - indicating a "pre-release" version
244
# ('alpha', 'beta', 'a', 'b', 'pre', 'p')
245
# - indicating a post-release patch ('p', 'pl', 'patch')
246
# but of course this can't cover all version number schemes, and there's
247
# no way to know what a programmer means without asking him.
248
#
249
# The problem is what to do with letters (and other non-numeric
250
# characters) in a version number. The current implementation does the
251
# obvious and predictable thing: keep them as strings and compare
252
# lexically within a tuple comparison. This has the desired effect if
253
# an appended letter sequence implies something "post-release":
254
# eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002".
255
#
256
# However, if letters in a version number imply a pre-release version,
257
# the "obvious" thing isn't correct. Eg. you would expect that
258
# "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison
259
# implemented here, this just isn't so.
260
#
261
# Two possible solutions come to mind. The first is to tie the
262
# comparison algorithm to a particular set of semantic rules, as has
263
# been done in the StrictVersion class above. This works great as long
264
# as everyone can go along with bondage and discipline. Hopefully a
265
# (large) subset of Python module programmers will agree that the
266
# particular flavour of bondage and discipline provided by StrictVersion
267
# provides enough benefit to be worth using, and will submit their
268
# version numbering scheme to its domination. The free-thinking
269
# anarchists in the lot will never give in, though, and something needs
270
# to be done to accommodate them.
271
#
272
# Perhaps a "moderately strict" version class could be implemented that
273
# lets almost anything slide (syntactically), and makes some heuristic
274
# assumptions about non-digits in version number strings. This could
275
# sink into special-case-hell, though; if I was as talented and
276
# idiosyncratic as Larry Wall, I'd go ahead and implement a class that
277
# somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is
278
# just as happy dealing with things like "2g6" and "1.13++". I don't
279
# think I'm smart enough to do it right though.
280
#
281
# In any case, I've coded the test suite for this module (see
282
# ../test/test_version.py) specifically to fail on things like comparing
283
# "1.2a2" and "1.2". That's not because the *code* is doing anything
284
# wrong, it's because the simple, obvious design doesn't match my
285
# complicated, hairy expectations for real-world version numbers. It
286
# would be a snap to fix the test suite to say, "Yep, LooseVersion does
287
# the Right Thing" (ie. the code matches the conception). But I'd rather
288
# have a conception that matches common notions about version numbers.
289
290
class LooseVersion (Version):
291
292
"""Version numbering for anarchists and software realists.
293
Implements the standard interface for version number classes as
294
described above. A version number consists of a series of numbers,
295
separated by either periods or strings of letters. When comparing
296
version numbers, the numeric components will be compared
297
numerically, and the alphabetic components lexically. The following
298
are all valid version numbers, in no particular order:
299
300
1.5.1
301
1.5.2b2
302
161
303
3.10a
304
8.02
305
3.4j
306
1996.07.12
307
3.2.pl0
308
3.1.1.6
309
2g6
310
11g
311
0.960923
312
2.2beta29
313
1.13++
314
5.5.kw
315
2.0b1pl0
316
317
In fact, there is no such thing as an invalid version number under
318
this scheme; the rules for comparison are simple and predictable,
319
but may not always give the results you want (for some definition
320
of "want").
321
"""
322
323
component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE)
324
325
def parse (self, vstring):
326
# I've given up on thinking I can reconstruct the version string
327
# from the parsed tuple -- so I just store the string here for
328
# use by __str__
329
self.vstring = vstring
330
components = [x for x in self.component_re.split(vstring)
331
if x and x != '.']
332
for i, obj in enumerate(components):
333
try:
334
components[i] = int(obj)
335
except ValueError:
336
pass
337
338
self.version = components
339
340
341
def __str__ (self):
342
return self.vstring
343
344
345
def __repr__ (self):
346
return "LooseVersion ('%s')" % str(self)
347
348
349
def _cmp (self, other):
350
if isinstance(other, str):
351
other = LooseVersion(other)
352
elif not isinstance(other, LooseVersion):
353
return NotImplemented
354
355
if self.version == other.version:
356
return 0
357
if self.version < other.version:
358
return -1
359
if self.version > other.version:
360
return 1
361
362
363
# end class LooseVersion
364
365