diff options
author | Shubham Saini <shubham6405@gmail.com> | 2019-08-05 08:32:33 +0000 |
---|---|---|
committer | Shubham Saini <shubham6405@gmail.com> | 2019-08-05 08:32:33 +0000 |
commit | 227b2d30a8675b44918f9d9ca89b24144a938215 (patch) | |
tree | 9f8e6a28724514b6fdf463a9ab2067a7ef309b72 /venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/distlib/version.py | |
parent | 842a8cfbbbdb1f92889d892e4859dbd5d40c5be8 (diff) |
removing venv files
Diffstat (limited to 'venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/distlib/version.py')
-rw-r--r-- | venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/distlib/version.py | 736 |
1 files changed, 0 insertions, 736 deletions
diff --git a/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/distlib/version.py b/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/distlib/version.py deleted file mode 100644 index 959f153..0000000 --- a/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/distlib/version.py +++ /dev/null | |||
@@ -1,736 +0,0 @@ | |||
1 | # -*- coding: utf-8 -*- | ||
2 | # | ||
3 | # Copyright (C) 2012-2017 The Python Software Foundation. | ||
4 | # See LICENSE.txt and CONTRIBUTORS.txt. | ||
5 | # | ||
6 | """ | ||
7 | Implementation of a flexible versioning scheme providing support for PEP-440, | ||
8 | setuptools-compatible and semantic versioning. | ||
9 | """ | ||
10 | |||
11 | import logging | ||
12 | import re | ||
13 | |||
14 | from .compat import string_types | ||
15 | from .util import parse_requirement | ||
16 | |||
17 | __all__ = ['NormalizedVersion', 'NormalizedMatcher', | ||
18 | 'LegacyVersion', 'LegacyMatcher', | ||
19 | 'SemanticVersion', 'SemanticMatcher', | ||
20 | 'UnsupportedVersionError', 'get_scheme'] | ||
21 | |||
22 | logger = logging.getLogger(__name__) | ||
23 | |||
24 | |||
25 | class UnsupportedVersionError(ValueError): | ||
26 | """This is an unsupported version.""" | ||
27 | pass | ||
28 | |||
29 | |||
30 | class Version(object): | ||
31 | def __init__(self, s): | ||
32 | self._string = s = s.strip() | ||
33 | self._parts = parts = self.parse(s) | ||
34 | assert isinstance(parts, tuple) | ||
35 | assert len(parts) > 0 | ||
36 | |||
37 | def parse(self, s): | ||
38 | raise NotImplementedError('please implement in a subclass') | ||
39 | |||
40 | def _check_compatible(self, other): | ||
41 | if type(self) != type(other): | ||
42 | raise TypeError('cannot compare %r and %r' % (self, other)) | ||
43 | |||
44 | def __eq__(self, other): | ||
45 | self._check_compatible(other) | ||
46 | return self._parts == other._parts | ||
47 | |||
48 | def __ne__(self, other): | ||
49 | return not self.__eq__(other) | ||
50 | |||
51 | def __lt__(self, other): | ||
52 | self._check_compatible(other) | ||
53 | return self._parts < other._parts | ||
54 | |||
55 | def __gt__(self, other): | ||
56 | return not (self.__lt__(other) or self.__eq__(other)) | ||
57 | |||
58 | def __le__(self, other): | ||
59 | return self.__lt__(other) or self.__eq__(other) | ||
60 | |||
61 | def __ge__(self, other): | ||
62 | return self.__gt__(other) or self.__eq__(other) | ||
63 | |||
64 | # See http://docs.python.org/reference/datamodel#object.__hash__ | ||
65 | def __hash__(self): | ||
66 | return hash(self._parts) | ||
67 | |||
68 | def __repr__(self): | ||
69 | return "%s('%s')" % (self.__class__.__name__, self._string) | ||
70 | |||
71 | def __str__(self): | ||
72 | return self._string | ||
73 | |||
74 | @property | ||
75 | def is_prerelease(self): | ||
76 | raise NotImplementedError('Please implement in subclasses.') | ||
77 | |||
78 | |||
79 | class Matcher(object): | ||
80 | version_class = None | ||
81 | |||
82 | # value is either a callable or the name of a method | ||
83 | _operators = { | ||
84 | '<': lambda v, c, p: v < c, | ||
85 | '>': lambda v, c, p: v > c, | ||
86 | '<=': lambda v, c, p: v == c or v < c, | ||
87 | '>=': lambda v, c, p: v == c or v > c, | ||
88 | '==': lambda v, c, p: v == c, | ||
89 | '===': lambda v, c, p: v == c, | ||
90 | # by default, compatible => >=. | ||
91 | '~=': lambda v, c, p: v == c or v > c, | ||
92 | '!=': lambda v, c, p: v != c, | ||
93 | } | ||
94 | |||
95 | # this is a method only to support alternative implementations | ||
96 | # via overriding | ||
97 | def parse_requirement(self, s): | ||
98 | return parse_requirement(s) | ||
99 | |||
100 | def __init__(self, s): | ||
101 | if self.version_class is None: | ||
102 | raise ValueError('Please specify a version class') | ||
103 | self._string = s = s.strip() | ||
104 | r = self.parse_requirement(s) | ||
105 | if not r: | ||
106 | raise ValueError('Not valid: %r' % s) | ||
107 | self.name = r.name | ||
108 | self.key = self.name.lower() # for case-insensitive comparisons | ||
109 | clist = [] | ||
110 | if r.constraints: | ||
111 | # import pdb; pdb.set_trace() | ||
112 | for op, s in r.constraints: | ||
113 | if s.endswith('.*'): | ||
114 | if op not in ('==', '!='): | ||
115 | raise ValueError('\'.*\' not allowed for ' | ||
116 | '%r constraints' % op) | ||
117 | # Could be a partial version (e.g. for '2.*') which | ||
118 | # won't parse as a version, so keep it as a string | ||
119 | vn, prefix = s[:-2], True | ||
120 | # Just to check that vn is a valid version | ||
121 | self.version_class(vn) | ||
122 | else: | ||
123 | # Should parse as a version, so we can create an | ||
124 | # instance for the comparison | ||
125 | vn, prefix = self.version_class(s), False | ||
126 | clist.append((op, vn, prefix)) | ||
127 | self._parts = tuple(clist) | ||
128 | |||
129 | def match(self, version): | ||
130 | """ | ||
131 | Check if the provided version matches the constraints. | ||
132 | |||
133 | :param version: The version to match against this instance. | ||
134 | :type version: String or :class:`Version` instance. | ||
135 | """ | ||
136 | if isinstance(version, string_types): | ||
137 | version = self.version_class(version) | ||
138 | for operator, constraint, prefix in self._parts: | ||
139 | f = self._operators.get(operator) | ||
140 | if isinstance(f, string_types): | ||
141 | f = getattr(self, f) | ||
142 | if not f: | ||
143 | msg = ('%r not implemented ' | ||
144 | 'for %s' % (operator, self.__class__.__name__)) | ||
145 | raise NotImplementedError(msg) | ||
146 | if not f(version, constraint, prefix): | ||
147 | return False | ||
148 | return True | ||
149 | |||
150 | @property | ||
151 | def exact_version(self): | ||
152 | result = None | ||
153 | if len(self._parts) == 1 and self._parts[0][0] in ('==', '==='): | ||
154 | result = self._parts[0][1] | ||
155 | return result | ||
156 | |||
157 | def _check_compatible(self, other): | ||
158 | if type(self) != type(other) or self.name != other.name: | ||
159 | raise TypeError('cannot compare %s and %s' % (self, other)) | ||
160 | |||
161 | def __eq__(self, other): | ||
162 | self._check_compatible(other) | ||
163 | return self.key == other.key and self._parts == other._parts | ||
164 | |||
165 | def __ne__(self, other): | ||
166 | return not self.__eq__(other) | ||
167 | |||
168 | # See http://docs.python.org/reference/datamodel#object.__hash__ | ||
169 | def __hash__(self): | ||
170 | return hash(self.key) + hash(self._parts) | ||
171 | |||
172 | def __repr__(self): | ||
173 | return "%s(%r)" % (self.__class__.__name__, self._string) | ||
174 | |||
175 | def __str__(self): | ||
176 | return self._string | ||
177 | |||
178 | |||
179 | PEP440_VERSION_RE = re.compile(r'^v?(\d+!)?(\d+(\.\d+)*)((a|b|c|rc)(\d+))?' | ||
180 | r'(\.(post)(\d+))?(\.(dev)(\d+))?' | ||
181 | r'(\+([a-zA-Z\d]+(\.[a-zA-Z\d]+)?))?$') | ||
182 | |||
183 | |||
184 | def _pep_440_key(s): | ||
185 | s = s.strip() | ||
186 | m = PEP440_VERSION_RE.match(s) | ||
187 | if not m: | ||
188 | raise UnsupportedVersionError('Not a valid version: %s' % s) | ||
189 | groups = m.groups() | ||
190 | nums = tuple(int(v) for v in groups[1].split('.')) | ||
191 | while len(nums) > 1 and nums[-1] == 0: | ||
192 | nums = nums[:-1] | ||
193 | |||
194 | if not groups[0]: | ||
195 | epoch = 0 | ||
196 | else: | ||
197 | epoch = int(groups[0]) | ||
198 | pre = groups[4:6] | ||
199 | post = groups[7:9] | ||
200 | dev = groups[10:12] | ||
201 | local = groups[13] | ||
202 | if pre == (None, None): | ||
203 | pre = () | ||
204 | else: | ||
205 | pre = pre[0], int(pre[1]) | ||
206 | if post == (None, None): | ||
207 | post = () | ||
208 | else: | ||
209 | post = post[0], int(post[1]) | ||
210 | if dev == (None, None): | ||
211 | dev = () | ||
212 | else: | ||
213 | dev = dev[0], int(dev[1]) | ||
214 | if local is None: | ||
215 | local = () | ||
216 | else: | ||
217 | parts = [] | ||
218 | for part in local.split('.'): | ||
219 | # to ensure that numeric compares as > lexicographic, avoid | ||
220 | # comparing them directly, but encode a tuple which ensures | ||
221 | # correct sorting | ||
222 | if part.isdigit(): | ||
223 | part = (1, int(part)) | ||
224 | else: | ||
225 | part = (0, part) | ||
226 | parts.append(part) | ||
227 | local = tuple(parts) | ||
228 | if not pre: | ||
229 | # either before pre-release, or final release and after | ||
230 | if not post and dev: | ||
231 | # before pre-release | ||
232 | pre = ('a', -1) # to sort before a0 | ||
233 | else: | ||
234 | pre = ('z',) # to sort after all pre-releases | ||
235 | # now look at the state of post and dev. | ||
236 | if not post: | ||
237 | post = ('_',) # sort before 'a' | ||
238 | if not dev: | ||
239 | dev = ('final',) | ||
240 | |||
241 | #print('%s -> %s' % (s, m.groups())) | ||
242 | return epoch, nums, pre, post, dev, local | ||
243 | |||
244 | |||
245 | _normalized_key = _pep_440_key | ||
246 | |||
247 | |||
248 | class NormalizedVersion(Version): | ||
249 | """A rational version. | ||
250 | |||
251 | Good: | ||
252 | 1.2 # equivalent to "1.2.0" | ||
253 | 1.2.0 | ||
254 | 1.2a1 | ||
255 | 1.2.3a2 | ||
256 | 1.2.3b1 | ||
257 | 1.2.3c1 | ||
258 | 1.2.3.4 | ||
259 | TODO: fill this out | ||
260 | |||
261 | Bad: | ||
262 | 1 # minimum two numbers | ||
263 | 1.2a # release level must have a release serial | ||
264 | 1.2.3b | ||
265 | """ | ||
266 | def parse(self, s): | ||
267 | result = _normalized_key(s) | ||
268 | # _normalized_key loses trailing zeroes in the release | ||
269 | # clause, since that's needed to ensure that X.Y == X.Y.0 == X.Y.0.0 | ||
270 | # However, PEP 440 prefix matching needs it: for example, | ||
271 | # (~= 1.4.5.0) matches differently to (~= 1.4.5.0.0). | ||
272 | m = PEP440_VERSION_RE.match(s) # must succeed | ||
273 | groups = m.groups() | ||
274 | self._release_clause = tuple(int(v) for v in groups[1].split('.')) | ||
275 | return result | ||
276 | |||
277 | PREREL_TAGS = set(['a', 'b', 'c', 'rc', 'dev']) | ||
278 | |||
279 | @property | ||
280 | def is_prerelease(self): | ||
281 | return any(t[0] in self.PREREL_TAGS for t in self._parts if t) | ||
282 | |||
283 | |||
284 | def _match_prefix(x, y): | ||
285 | x = str(x) | ||
286 | y = str(y) | ||
287 | if x == y: | ||
288 | return True | ||
289 | if not x.startswith(y): | ||
290 | return False | ||
291 | n = len(y) | ||
292 | return x[n] == '.' | ||
293 | |||
294 | |||
295 | class NormalizedMatcher(Matcher): | ||
296 | version_class = NormalizedVersion | ||
297 | |||
298 | # value is either a callable or the name of a method | ||
299 | _operators = { | ||
300 | '~=': '_match_compatible', | ||
301 | '<': '_match_lt', | ||
302 | '>': '_match_gt', | ||
303 | '<=': '_match_le', | ||
304 | '>=': '_match_ge', | ||
305 | '==': '_match_eq', | ||
306 | '===': '_match_arbitrary', | ||
307 | '!=': '_match_ne', | ||
308 | } | ||
309 | |||
310 | def _adjust_local(self, version, constraint, prefix): | ||
311 | if prefix: | ||
312 | strip_local = '+' not in constraint and version._parts[-1] | ||
313 | else: | ||
314 | # both constraint and version are | ||
315 | # NormalizedVersion instances. | ||
316 | # If constraint does not have a local component, | ||
317 | # ensure the version doesn't, either. | ||
318 | strip_local = not constraint._parts[-1] and version._parts[-1] | ||
319 | if strip_local: | ||
320 | s = version._string.split('+', 1)[0] | ||
321 | version = self.version_class(s) | ||
322 | return version, constraint | ||
323 | |||
324 | def _match_lt(self, version, constraint, prefix): | ||
325 | version, constraint = self._adjust_local(version, constraint, prefix) | ||
326 | if version >= constraint: | ||
327 | return False | ||
328 | release_clause = constraint._release_clause | ||
329 | pfx = '.'.join([str(i) for i in release_clause]) | ||
330 | return not _match_prefix(version, pfx) | ||
331 | |||
332 | def _match_gt(self, version, constraint, prefix): | ||
333 | version, constraint = self._adjust_local(version, constraint, prefix) | ||
334 | if version <= constraint: | ||
335 | return False | ||
336 | release_clause = constraint._release_clause | ||
337 | pfx = '.'.join([str(i) for i in release_clause]) | ||
338 | return not _match_prefix(version, pfx) | ||
339 | |||
340 | def _match_le(self, version, constraint, prefix): | ||
341 | version, constraint = self._adjust_local(version, constraint, prefix) | ||
342 | return version <= constraint | ||
343 | |||
344 | def _match_ge(self, version, constraint, prefix): | ||
345 | version, constraint = self._adjust_local(version, constraint, prefix) | ||
346 | return version >= constraint | ||
347 | |||
348 | def _match_eq(self, version, constraint, prefix): | ||
349 | version, constraint = self._adjust_local(version, constraint, prefix) | ||
350 | if not prefix: | ||
351 | result = (version == constraint) | ||
352 | else: | ||
353 | result = _match_prefix(version, constraint) | ||
354 | return result | ||
355 | |||
356 | def _match_arbitrary(self, version, constraint, prefix): | ||
357 | return str(version) == str(constraint) | ||
358 | |||
359 | def _match_ne(self, version, constraint, prefix): | ||
360 | version, constraint = self._adjust_local(version, constraint, prefix) | ||
361 | if not prefix: | ||
362 | result = (version != constraint) | ||
363 | else: | ||
364 | result = not _match_prefix(version, constraint) | ||
365 | return result | ||
366 | |||
367 | def _match_compatible(self, version, constraint, prefix): | ||
368 | version, constraint = self._adjust_local(version, constraint, prefix) | ||
369 | if version == constraint: | ||
370 | return True | ||
371 | if version < constraint: | ||
372 | return False | ||
373 | # if not prefix: | ||
374 | # return True | ||
375 | release_clause = constraint._release_clause | ||
376 | if len(release_clause) > 1: | ||
377 | release_clause = release_clause[:-1] | ||
378 | pfx = '.'.join([str(i) for i in release_clause]) | ||
379 | return _match_prefix(version, pfx) | ||
380 | |||
381 | _REPLACEMENTS = ( | ||
382 | (re.compile('[.+-]$'), ''), # remove trailing puncts | ||
383 | (re.compile(r'^[.](\d)'), r'0.\1'), # .N -> 0.N at start | ||
384 | (re.compile('^[.-]'), ''), # remove leading puncts | ||
385 | (re.compile(r'^\((.*)\)$'), r'\1'), # remove parentheses | ||
386 | (re.compile(r'^v(ersion)?\s*(\d+)'), r'\2'), # remove leading v(ersion) | ||
387 | (re.compile(r'^r(ev)?\s*(\d+)'), r'\2'), # remove leading v(ersion) | ||
388 | (re.compile('[.]{2,}'), '.'), # multiple runs of '.' | ||
389 | (re.compile(r'\b(alfa|apha)\b'), 'alpha'), # misspelt alpha | ||
390 | (re.compile(r'\b(pre-alpha|prealpha)\b'), | ||
391 | 'pre.alpha'), # standardise | ||
392 | (re.compile(r'\(beta\)$'), 'beta'), # remove parentheses | ||
393 | ) | ||
394 | |||
395 | _SUFFIX_REPLACEMENTS = ( | ||
396 | (re.compile('^[:~._+-]+'), ''), # remove leading puncts | ||
397 | (re.compile('[,*")([\\]]'), ''), # remove unwanted chars | ||
398 | (re.compile('[~:+_ -]'), '.'), # replace illegal chars | ||
399 | (re.compile('[.]{2,}'), '.'), # multiple runs of '.' | ||
400 | (re.compile(r'\.$'), ''), # trailing '.' | ||
401 | ) | ||
402 | |||
403 | _NUMERIC_PREFIX = re.compile(r'(\d+(\.\d+)*)') | ||
404 | |||
405 | |||
406 | def _suggest_semantic_version(s): | ||
407 | """ | ||
408 | Try to suggest a semantic form for a version for which | ||
409 | _suggest_normalized_version couldn't come up with anything. | ||
410 | """ | ||
411 | result = s.strip().lower() | ||
412 | for pat, repl in _REPLACEMENTS: | ||
413 | result = pat.sub(repl, result) | ||
414 | if not result: | ||
415 | result = '0.0.0' | ||
416 | |||
417 | # Now look for numeric prefix, and separate it out from | ||
418 | # the rest. | ||
419 | #import pdb; pdb.set_trace() | ||
420 | m = _NUMERIC_PREFIX.match(result) | ||
421 | if not m: | ||
422 | prefix = '0.0.0' | ||
423 | suffix = result | ||
424 | else: | ||
425 | prefix = m.groups()[0].split('.') | ||
426 | prefix = [int(i) for i in prefix] | ||
427 | while len(prefix) < 3: | ||
428 | prefix.append(0) | ||
429 | if len(prefix) == 3: | ||
430 | suffix = result[m.end():] | ||
431 | else: | ||
432 | suffix = '.'.join([str(i) for i in prefix[3:]]) + result[m.end():] | ||
433 | prefix = prefix[:3] | ||
434 | prefix = '.'.join([str(i) for i in prefix]) | ||
435 | suffix = suffix.strip() | ||
436 | if suffix: | ||
437 | #import pdb; pdb.set_trace() | ||
438 | # massage the suffix. | ||
439 | for pat, repl in _SUFFIX_REPLACEMENTS: | ||
440 | suffix = pat.sub(repl, suffix) | ||
441 | |||
442 | if not suffix: | ||
443 | result = prefix | ||
444 | else: | ||
445 | sep = '-' if 'dev' in suffix else '+' | ||
446 | result = prefix + sep + suffix | ||
447 | if not is_semver(result): | ||
448 | result = None | ||
449 | return result | ||
450 | |||
451 | |||
452 | def _suggest_normalized_version(s): | ||
453 | """Suggest a normalized version close to the given version string. | ||
454 | |||
455 | If you have a version string that isn't rational (i.e. NormalizedVersion | ||
456 | doesn't like it) then you might be able to get an equivalent (or close) | ||
457 | rational version from this function. | ||
458 | |||
459 | This does a number of simple normalizations to the given string, based | ||
460 | on observation of versions currently in use on PyPI. Given a dump of | ||
461 | those version during PyCon 2009, 4287 of them: | ||
462 | - 2312 (53.93%) match NormalizedVersion without change | ||
463 | with the automatic suggestion | ||
464 | - 3474 (81.04%) match when using this suggestion method | ||
465 | |||
466 | @param s {str} An irrational version string. | ||
467 | @returns A rational version string, or None, if couldn't determine one. | ||
468 | """ | ||
469 | try: | ||
470 | _normalized_key(s) | ||
471 | return s # already rational | ||
472 | except UnsupportedVersionError: | ||
473 | pass | ||
474 | |||
475 | rs = s.lower() | ||
476 | |||
477 | # part of this could use maketrans | ||
478 | for orig, repl in (('-alpha', 'a'), ('-beta', 'b'), ('alpha', 'a'), | ||
479 | ('beta', 'b'), ('rc', 'c'), ('-final', ''), | ||
480 | ('-pre', 'c'), | ||
481 | ('-release', ''), ('.release', ''), ('-stable', ''), | ||
482 | ('+', '.'), ('_', '.'), (' ', ''), ('.final', ''), | ||
483 | ('final', '')): | ||
484 | rs = rs.replace(orig, repl) | ||
485 | |||
486 | # if something ends with dev or pre, we add a 0 | ||
487 | rs = re.sub(r"pre$", r"pre0", rs) | ||
488 | rs = re.sub(r"dev$", r"dev0", rs) | ||
489 | |||
490 | # if we have something like "b-2" or "a.2" at the end of the | ||
491 | # version, that is probably beta, alpha, etc | ||
492 | # let's remove the dash or dot | ||
493 | rs = re.sub(r"([abc]|rc)[\-\.](\d+)$", r"\1\2", rs) | ||
494 | |||
495 | # 1.0-dev-r371 -> 1.0.dev371 | ||
496 | # 0.1-dev-r79 -> 0.1.dev79 | ||
497 | rs = re.sub(r"[\-\.](dev)[\-\.]?r?(\d+)$", r".\1\2", rs) | ||
498 | |||
499 | # Clean: 2.0.a.3, 2.0.b1, 0.9.0~c1 | ||
500 | rs = re.sub(r"[.~]?([abc])\.?", r"\1", rs) | ||
501 | |||
502 | # Clean: v0.3, v1.0 | ||
503 | if rs.startswith('v'): | ||
504 | rs = rs[1:] | ||
505 | |||
506 | # Clean leading '0's on numbers. | ||
507 | #TODO: unintended side-effect on, e.g., "2003.05.09" | ||
508 | # PyPI stats: 77 (~2%) better | ||
509 | rs = re.sub(r"\b0+(\d+)(?!\d)", r"\1", rs) | ||
510 | |||
511 | # Clean a/b/c with no version. E.g. "1.0a" -> "1.0a0". Setuptools infers | ||
512 | # zero. | ||
513 | # PyPI stats: 245 (7.56%) better | ||
514 | rs = re.sub(r"(\d+[abc])$", r"\g<1>0", rs) | ||
515 | |||
516 | # the 'dev-rNNN' tag is a dev tag | ||
517 | rs = re.sub(r"\.?(dev-r|dev\.r)\.?(\d+)$", r".dev\2", rs) | ||
518 | |||
519 | # clean the - when used as a pre delimiter | ||
520 | rs = re.sub(r"-(a|b|c)(\d+)$", r"\1\2", rs) | ||
521 | |||
522 | # a terminal "dev" or "devel" can be changed into ".dev0" | ||
523 | rs = re.sub(r"[\.\-](dev|devel)$", r".dev0", rs) | ||
524 | |||
525 | # a terminal "dev" can be changed into ".dev0" | ||
526 | rs = re.sub(r"(?![\.\-])dev$", r".dev0", rs) | ||
527 | |||
528 | # a terminal "final" or "stable" can be removed | ||
529 | rs = re.sub(r"(final|stable)$", "", rs) | ||
530 | |||
531 | # The 'r' and the '-' tags are post release tags | ||
532 | # 0.4a1.r10 -> 0.4a1.post10 | ||
533 | # 0.9.33-17222 -> 0.9.33.post17222 | ||
534 | # 0.9.33-r17222 -> 0.9.33.post17222 | ||
535 | rs = re.sub(r"\.?(r|-|-r)\.?(\d+)$", r".post\2", rs) | ||
536 | |||
537 | # Clean 'r' instead of 'dev' usage: | ||
538 | # 0.9.33+r17222 -> 0.9.33.dev17222 | ||
539 | # 1.0dev123 -> 1.0.dev123 | ||
540 | # 1.0.git123 -> 1.0.dev123 | ||
541 | # 1.0.bzr123 -> 1.0.dev123 | ||
542 | # 0.1a0dev.123 -> 0.1a0.dev123 | ||
543 | # PyPI stats: ~150 (~4%) better | ||
544 | rs = re.sub(r"\.?(dev|git|bzr)\.?(\d+)$", r".dev\2", rs) | ||
545 | |||
546 | # Clean '.pre' (normalized from '-pre' above) instead of 'c' usage: | ||
547 | # 0.2.pre1 -> 0.2c1 | ||
548 | # 0.2-c1 -> 0.2c1 | ||
549 | # 1.0preview123 -> 1.0c123 | ||
550 | # PyPI stats: ~21 (0.62%) better | ||
551 | rs = re.sub(r"\.?(pre|preview|-c)(\d+)$", r"c\g<2>", rs) | ||
552 | |||
553 | # Tcl/Tk uses "px" for their post release markers | ||
554 | rs = re.sub(r"p(\d+)$", r".post\1", rs) | ||
555 | |||
556 | try: | ||
557 | _normalized_key(rs) | ||
558 | except UnsupportedVersionError: | ||
559 | rs = None | ||
560 | return rs | ||
561 | |||
562 | # | ||
563 | # Legacy version processing (distribute-compatible) | ||
564 | # | ||
565 | |||
566 | _VERSION_PART = re.compile(r'([a-z]+|\d+|[\.-])', re.I) | ||
567 | _VERSION_REPLACE = { | ||
568 | 'pre': 'c', | ||
569 | 'preview': 'c', | ||
570 | '-': 'final-', | ||
571 | 'rc': 'c', | ||
572 | 'dev': '@', | ||
573 | '': None, | ||
574 | '.': None, | ||
575 | } | ||
576 | |||
577 | |||
578 | def _legacy_key(s): | ||
579 | def get_parts(s): | ||
580 | result = [] | ||
581 | for p in _VERSION_PART.split(s.lower()): | ||
582 | p = _VERSION_REPLACE.get(p, p) | ||
583 | if p: | ||
584 | if '0' <= p[:1] <= '9': | ||
585 | p = p.zfill(8) | ||
586 | else: | ||
587 | p = '*' + p | ||
588 | result.append(p) | ||
589 | result.append('*final') | ||
590 | return result | ||
591 | |||
592 | result = [] | ||
593 | for p in get_parts(s): | ||
594 | if p.startswith('*'): | ||
595 | if p < '*final': | ||
596 | while result and result[-1] == '*final-': | ||
597 | result.pop() | ||
598 | while result and result[-1] == '00000000': | ||
599 | result.pop() | ||
600 | result.append(p) | ||
601 | return tuple(result) | ||
602 | |||
603 | |||
604 | class LegacyVersion(Version): | ||
605 | def parse(self, s): | ||
606 | return _legacy_key(s) | ||
607 | |||
608 | @property | ||
609 | def is_prerelease(self): | ||
610 | result = False | ||
611 | for x in self._parts: | ||
612 | if (isinstance(x, string_types) and x.startswith('*') and | ||
613 | x < '*final'): | ||
614 | result = True | ||
615 | break | ||
616 | return result | ||
617 | |||
618 | |||
619 | class LegacyMatcher(Matcher): | ||
620 | version_class = LegacyVersion | ||
621 | |||
622 | _operators = dict(Matcher._operators) | ||
623 | _operators['~='] = '_match_compatible' | ||
624 | |||
625 | numeric_re = re.compile(r'^(\d+(\.\d+)*)') | ||
626 | |||
627 | def _match_compatible(self, version, constraint, prefix): | ||
628 | if version < constraint: | ||
629 | return False | ||
630 | m = self.numeric_re.match(str(constraint)) | ||
631 | if not m: | ||
632 | logger.warning('Cannot compute compatible match for version %s ' | ||
633 | ' and constraint %s', version, constraint) | ||
634 | return True | ||
635 | s = m.groups()[0] | ||
636 | if '.' in s: | ||
637 | s = s.rsplit('.', 1)[0] | ||
638 | return _match_prefix(version, s) | ||
639 | |||
640 | # | ||
641 | # Semantic versioning | ||
642 | # | ||
643 | |||
644 | _SEMVER_RE = re.compile(r'^(\d+)\.(\d+)\.(\d+)' | ||
645 | r'(-[a-z0-9]+(\.[a-z0-9-]+)*)?' | ||
646 | r'(\+[a-z0-9]+(\.[a-z0-9-]+)*)?$', re.I) | ||
647 | |||
648 | |||
649 | def is_semver(s): | ||
650 | return _SEMVER_RE.match(s) | ||
651 | |||
652 | |||
653 | def _semantic_key(s): | ||
654 | def make_tuple(s, absent): | ||
655 | if s is None: | ||
656 | result = (absent,) | ||
657 | else: | ||
658 | parts = s[1:].split('.') | ||
659 | # We can't compare ints and strings on Python 3, so fudge it | ||
660 | # by zero-filling numeric values so simulate a numeric comparison | ||
661 | result = tuple([p.zfill(8) if p.isdigit() else p for p in parts]) | ||
662 | return result | ||
663 | |||
664 | m = is_semver(s) | ||
665 | if not m: | ||
666 | raise UnsupportedVersionError(s) | ||
667 | groups = m.groups() | ||
668 | major, minor, patch = [int(i) for i in groups[:3]] | ||
669 | # choose the '|' and '*' so that versions sort correctly | ||
670 | pre, build = make_tuple(groups[3], '|'), make_tuple(groups[5], '*') | ||
671 | return (major, minor, patch), pre, build | ||
672 | |||
673 | |||
674 | class SemanticVersion(Version): | ||
675 | def parse(self, s): | ||
676 | return _semantic_key(s) | ||
677 | |||
678 | @property | ||
679 | def is_prerelease(self): | ||
680 | return self._parts[1][0] != '|' | ||
681 | |||
682 | |||
683 | class SemanticMatcher(Matcher): | ||
684 | version_class = SemanticVersion | ||
685 | |||
686 | |||
687 | class VersionScheme(object): | ||
688 | def __init__(self, key, matcher, suggester=None): | ||
689 | self.key = key | ||
690 | self.matcher = matcher | ||
691 | self.suggester = suggester | ||
692 | |||
693 | def is_valid_version(self, s): | ||
694 | try: | ||
695 | self.matcher.version_class(s) | ||
696 | result = True | ||
697 | except UnsupportedVersionError: | ||
698 | result = False | ||
699 | return result | ||
700 | |||
701 | def is_valid_matcher(self, s): | ||
702 | try: | ||
703 | self.matcher(s) | ||
704 | result = True | ||
705 | except UnsupportedVersionError: | ||
706 | result = False | ||
707 | return result | ||
708 | |||
709 | def is_valid_constraint_list(self, s): | ||
710 | """ | ||
711 | Used for processing some metadata fields | ||
712 | """ | ||
713 | return self.is_valid_matcher('dummy_name (%s)' % s) | ||
714 | |||
715 | def suggest(self, s): | ||
716 | if self.suggester is None: | ||
717 | result = None | ||
718 | else: | ||
719 | result = self.suggester(s) | ||
720 | return result | ||
721 | |||
722 | _SCHEMES = { | ||
723 | 'normalized': VersionScheme(_normalized_key, NormalizedMatcher, | ||
724 | _suggest_normalized_version), | ||
725 | 'legacy': VersionScheme(_legacy_key, LegacyMatcher, lambda self, s: s), | ||
726 | 'semantic': VersionScheme(_semantic_key, SemanticMatcher, | ||
727 | _suggest_semantic_version), | ||
728 | } | ||
729 | |||
730 | _SCHEMES['default'] = _SCHEMES['normalized'] | ||
731 | |||
732 | |||
733 | def get_scheme(name): | ||
734 | if name not in _SCHEMES: | ||
735 | raise ValueError('unknown scheme name: %r' % name) | ||
736 | return _SCHEMES[name] | ||