7Implementation of a flexible versioning scheme providing support for PEP-440,
8setuptools-compatible and semantic versioning.
14from .compat
import string_types
15from .util
import parse_requirement
17__all__ = [
'NormalizedVersion',
'NormalizedMatcher',
18 'LegacyVersion',
'LegacyMatcher',
19 'SemanticVersion',
'SemanticMatcher',
20 'UnsupportedVersionError',
'get_scheme']
26 """This is an unsupported version."""
41 if type(self) != type(other):
42 raise TypeError(
'cannot compare %r and %r' % (self, other))
49 return not self.
__eq__(other)
69 return "%s('%s')" % (self.__class__.__name__, self.
_string)
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,
91 '~=':
lambda v, c, p: v == c
or v > c,
92 '!=':
lambda v, c, p: v != c,
97 def parse_requirement(self, s):
98 return parse_requirement(s)
102 raise ValueError(
'Please specify a version class')
106 raise ValueError(
'Not valid: %r' % s)
114 if op
not in (
'==',
'!='):
115 raise ValueError(
'\'.*\' not allowed for '
116 '%r constraints' % op)
119 vn, prefix = s[:-2],
True
131 Check if the provided version matches the constraints.
133 :param version: The version to match against this instance.
134 :type version: String or :class:`Version` instance.
138 for operator, constraint, prefix
in self.
_parts:
143 msg = (
'%r not implemented '
144 'for %s' % (operator, self.__class__.__name__))
146 if not f(version, constraint, prefix):
154 result = self.
_parts[0][1]
159 raise TypeError(
'cannot compare %s and %s' % (self, other))
166 return not self.
__eq__(other)
170 return hash(self.
key) + hash(self.
_parts)
173 return "%s(%r)" % (self.__class__.__name__, self.
_string)
179PEP440_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]+)?))?$')
190 nums = tuple(int(v)
for v
in groups[1].split(
'.'))
191 while len(nums) > 1
and nums[-1] == 0:
197 epoch = int(groups[0][:-1])
202 if pre == (
None,
None):
205 pre = pre[0], int(pre[1])
206 if post == (
None,
None):
209 post = post[0], int(post[1])
210 if dev == (
None,
None):
213 dev = dev[0], int(dev[1])
223 part = (1, int(part))
242 return epoch, nums, pre, post, dev, local
245_normalized_key = _pep_440_key
249 """A rational version.
252 1.2 # equivalent to "1.2.0"
262 1 # minimum two numbers
263 1.2a # release level must have a release serial
277 PREREL_TAGS = set([
'a',
'b',
'c',
'rc',
'dev'])
296 version_class = NormalizedVersion
300 '~=':
'_match_compatible',
306 '===':
'_match_arbitrary',
322 return version, constraint
325 version, constraint = self.
_adjust_local(version, constraint, prefix)
326 if version >= constraint:
329 pfx =
'.'.join([str(i)
for i
in release_clause])
333 version, constraint = self.
_adjust_local(version, constraint, prefix)
334 if version <= constraint:
337 pfx =
'.'.join([str(i)
for i
in release_clause])
341 version, constraint = self.
_adjust_local(version, constraint, prefix)
342 return version <= constraint
345 version, constraint = self.
_adjust_local(version, constraint, prefix)
346 return version >= constraint
349 version, constraint = self.
_adjust_local(version, constraint, prefix)
351 result = (version == constraint)
357 return str(version) == str(constraint)
360 version, constraint = self.
_adjust_local(version, constraint, prefix)
362 result = (version != constraint)
368 version, constraint = self.
_adjust_local(version, constraint, prefix)
369 if version == constraint:
371 if version < constraint:
376 if len(release_clause) > 1:
377 release_clause = release_clause[:-1]
378 pfx =
'.'.join([str(i)
for i
in release_clause])
395_SUFFIX_REPLACEMENTS = (
408 Try to suggest a semantic form for a version for which
409 _suggest_normalized_version couldn't come up with anything.
412 for pat, repl
in _REPLACEMENTS:
426 prefix = [int(i)
for i
in prefix]
427 while len(prefix) < 3:
430 suffix = result[
m.end():]
432 suffix =
'.'.join([str(i)
for i
in prefix[3:]]) + result[
m.end():]
434 prefix =
'.'.join([str(i)
for i
in prefix])
439 for pat, repl
in _SUFFIX_REPLACEMENTS:
445 sep =
'-' if 'dev' in suffix
else '+'
446 result = prefix + sep + suffix
453 """Suggest a normalized version close to the given version string.
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.
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
466 @param s {str} An irrational version string.
467 @returns A rational version string, or None, if couldn't determine one.
472 except UnsupportedVersionError:
478 for orig, repl
in ((
'-alpha',
'a'), (
'-beta',
'b'), (
'alpha',
'a'),
479 (
'beta',
'b'), (
'rc',
'c'), (
'-final',
''),
481 (
'-release',
''), (
'.release',
''), (
'-stable',
''),
482 (
'+',
'.'), (
'_',
'.'), (
' ',
''), (
'.final',
''),
487 rs =
re.sub(
r"pre$",
r"pre0", rs)
488 rs =
re.sub(
r"dev$",
r"dev0", rs)
493 rs =
re.sub(
r"([abc]|rc)[\-\.](\d+)$",
r"\1\2", rs)
497 rs =
re.sub(
r"[\-\.](dev)[\-\.]?r?(\d+)$",
r".\1\2", rs)
500 rs =
re.sub(
r"[.~]?([abc])\.?",
r"\1", rs)
509 rs =
re.sub(
r"\b0+(\d+)(?!\d)",
r"\1", rs)
514 rs =
re.sub(
r"(\d+[abc])$",
r"\g<1>0", rs)
517 rs =
re.sub(
r"\.?(dev-r|dev\.r)\.?(\d+)$",
r".dev\2", rs)
520 rs =
re.sub(
r"-(a|b|c)(\d+)$",
r"\1\2", rs)
523 rs =
re.sub(
r"[\.\-](dev|devel)$",
r".dev0", rs)
526 rs =
re.sub(
r"(?![\.\-])dev$",
r".dev0", rs)
529 rs =
re.sub(
r"(final|stable)$",
"", rs)
535 rs =
re.sub(
r"\.?(r|-|-r)\.?(\d+)$",
r".post\2", rs)
544 rs =
re.sub(
r"\.?(dev|git|bzr)\.?(\d+)$",
r".dev\2", rs)
551 rs =
re.sub(
r"\.?(pre|preview|-c)(\d+)$",
r"c\g<2>", rs)
554 rs =
re.sub(
r"p(\d+)$",
r".post\1", rs)
558 except UnsupportedVersionError:
584 if '0' <= p[:1] <=
'9':
596 while result
and result[-1] ==
'*final-':
598 while result
and result[-1] ==
'00000000':
620 version_class = LegacyVersion
623 _operators[
'~='] =
'_match_compatible'
628 if version < constraint:
633 ' and constraint %s', version, constraint)
645 r'(-[a-z0-9]+(\.[a-z0-9-]+)*)?'
646 r'(\+[a-z0-9]+(\.[a-z0-9-]+)*)?$',
re.I)
658 parts = s[1:].split(
'.')
668 major, minor, patch = [int(i)
for i
in groups[:3]]
671 return (major, minor, patch), pre, build
680 return self.
_parts[1][0] !=
'|'
684 version_class = SemanticVersion
697 except UnsupportedVersionError:
705 except UnsupportedVersionError:
711 Used for processing some metadata fields
726 'normalized':
VersionScheme(_normalized_key, NormalizedMatcher,
727 _suggest_normalized_version),
728 'legacy':
VersionScheme(_legacy_key, LegacyMatcher,
lambda self, s: s),
730 _suggest_semantic_version),
733_SCHEMES[
'default'] = _SCHEMES[
'normalized']
737 if name
not in _SCHEMES:
738 raise ValueError(
'unknown scheme name: %r' % name)
739 return _SCHEMES[name]
_match_compatible(self, version, constraint, prefix)
_check_compatible(self, other)
parse_requirement(self, s)
_match_compatible(self, version, constraint, prefix)
_adjust_local(self, version, constraint, prefix)
_match_ne(self, version, constraint, prefix)
_match_le(self, version, constraint, prefix)
_match_eq(self, version, constraint, prefix)
_match_arbitrary(self, version, constraint, prefix)
_match_ge(self, version, constraint, prefix)
_match_gt(self, version, constraint, prefix)
_match_lt(self, version, constraint, prefix)
is_valid_matcher(self, s)
is_valid_version(self, s)
__init__(self, key, matcher, suggester=None)
is_valid_constraint_list(self, s)
_check_compatible(self, other)
_suggest_normalized_version(s)
_suggest_semantic_version(s)