mirror of
https://github.com/kennethreitz/pipenv.git
synced 2026-06-05 22:50:18 +00:00
Vendored
+360
@@ -0,0 +1,360 @@
|
||||
"""
|
||||
Python helper for Semantic Versioning (http://semver.org/)
|
||||
"""
|
||||
|
||||
import collections
|
||||
import re
|
||||
|
||||
|
||||
__version__ = '2.7.8'
|
||||
__author__ = 'Kostiantyn Rybnikov'
|
||||
__author_email__ = 'k-bx@k-bx.com'
|
||||
|
||||
_REGEX = re.compile(
|
||||
r"""
|
||||
^
|
||||
(?P<major>(?:0|[1-9][0-9]*))
|
||||
\.
|
||||
(?P<minor>(?:0|[1-9][0-9]*))
|
||||
\.
|
||||
(?P<patch>(?:0|[1-9][0-9]*))
|
||||
(\-(?P<prerelease>
|
||||
(?:0|[1-9A-Za-z-][0-9A-Za-z-]*)
|
||||
(\.(?:0|[1-9A-Za-z-][0-9A-Za-z-]*))*
|
||||
))?
|
||||
(\+(?P<build>
|
||||
[0-9A-Za-z-]+
|
||||
(\.[0-9A-Za-z-]+)*
|
||||
))?
|
||||
$
|
||||
""", re.VERBOSE)
|
||||
|
||||
_LAST_NUMBER = re.compile(r'(?:[^\d]*(\d+)[^\d]*)+')
|
||||
|
||||
if not hasattr(__builtins__, 'cmp'):
|
||||
def cmp(a, b):
|
||||
return (a > b) - (a < b)
|
||||
|
||||
|
||||
def parse(version):
|
||||
"""Parse version to major, minor, patch, pre-release, build parts.
|
||||
|
||||
:param version: version string
|
||||
:return: dictionary with the keys 'build', 'major', 'minor', 'patch',
|
||||
and 'prerelease'. The prerelease or build keys can be None
|
||||
if not provided
|
||||
:rtype: dict
|
||||
"""
|
||||
match = _REGEX.match(version)
|
||||
if match is None:
|
||||
raise ValueError('%s is not valid SemVer string' % version)
|
||||
|
||||
version_parts = match.groupdict()
|
||||
|
||||
version_parts['major'] = int(version_parts['major'])
|
||||
version_parts['minor'] = int(version_parts['minor'])
|
||||
version_parts['patch'] = int(version_parts['patch'])
|
||||
|
||||
return version_parts
|
||||
|
||||
|
||||
class VersionInfo(collections.namedtuple(
|
||||
'VersionInfo', 'major minor patch prerelease build')):
|
||||
"""
|
||||
:param int major: version when you make incompatible API changes.
|
||||
:param int minor: version when you add functionality in
|
||||
a backwards-compatible manner.
|
||||
:param int patch: version when you make backwards-compatible bug fixes.
|
||||
:param str prerelease: an optional prerelease string
|
||||
:param str build: an optional build string
|
||||
|
||||
>>> import semver
|
||||
>>> ver = semver.parse('3.4.5-pre.2+build.4')
|
||||
>>> ver
|
||||
{'build': 'build.4', 'major': 3, 'minor': 4, 'patch': 5,
|
||||
'prerelease': 'pre.2'}
|
||||
"""
|
||||
__slots__ = ()
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, (VersionInfo, dict)):
|
||||
return NotImplemented
|
||||
return _compare_by_keys(self._asdict(), _to_dict(other)) == 0
|
||||
|
||||
def __ne__(self, other):
|
||||
if not isinstance(other, (VersionInfo, dict)):
|
||||
return NotImplemented
|
||||
return _compare_by_keys(self._asdict(), _to_dict(other)) != 0
|
||||
|
||||
def __lt__(self, other):
|
||||
if not isinstance(other, (VersionInfo, dict)):
|
||||
return NotImplemented
|
||||
return _compare_by_keys(self._asdict(), _to_dict(other)) < 0
|
||||
|
||||
def __le__(self, other):
|
||||
if not isinstance(other, (VersionInfo, dict)):
|
||||
return NotImplemented
|
||||
return _compare_by_keys(self._asdict(), _to_dict(other)) <= 0
|
||||
|
||||
def __gt__(self, other):
|
||||
if not isinstance(other, (VersionInfo, dict)):
|
||||
return NotImplemented
|
||||
return _compare_by_keys(self._asdict(), _to_dict(other)) > 0
|
||||
|
||||
def __ge__(self, other):
|
||||
if not isinstance(other, (VersionInfo, dict)):
|
||||
return NotImplemented
|
||||
return _compare_by_keys(self._asdict(), _to_dict(other)) >= 0
|
||||
|
||||
|
||||
def _to_dict(obj):
|
||||
if isinstance(obj, VersionInfo):
|
||||
return obj._asdict()
|
||||
return obj
|
||||
|
||||
|
||||
def parse_version_info(version):
|
||||
"""Parse version string to a VersionInfo instance.
|
||||
|
||||
:param version: version string
|
||||
:return: a :class:`VersionInfo` instance
|
||||
:rtype: :class:`VersionInfo`
|
||||
"""
|
||||
parts = parse(version)
|
||||
version_info = VersionInfo(
|
||||
parts['major'], parts['minor'], parts['patch'],
|
||||
parts['prerelease'], parts['build'])
|
||||
|
||||
return version_info
|
||||
|
||||
|
||||
def _nat_cmp(a, b):
|
||||
def convert(text):
|
||||
return int(text) if re.match('^[0-9]+$', text) else text
|
||||
|
||||
def split_key(key):
|
||||
return [convert(c) for c in key.split('.')]
|
||||
|
||||
def cmp_prerelease_tag(a, b):
|
||||
if isinstance(a, int) and isinstance(b, int):
|
||||
return cmp(a, b)
|
||||
elif isinstance(a, int):
|
||||
return -1
|
||||
elif isinstance(b, int):
|
||||
return 1
|
||||
else:
|
||||
return cmp(a, b)
|
||||
|
||||
a, b = a or '', b or ''
|
||||
a_parts, b_parts = split_key(a), split_key(b)
|
||||
for sub_a, sub_b in zip(a_parts, b_parts):
|
||||
cmp_result = cmp_prerelease_tag(sub_a, sub_b)
|
||||
if cmp_result != 0:
|
||||
return cmp_result
|
||||
else:
|
||||
return cmp(len(a), len(b))
|
||||
|
||||
|
||||
def _compare_by_keys(d1, d2):
|
||||
for key in ['major', 'minor', 'patch']:
|
||||
v = cmp(d1.get(key), d2.get(key))
|
||||
if v:
|
||||
return v
|
||||
|
||||
rc1, rc2 = d1.get('prerelease'), d2.get('prerelease')
|
||||
rccmp = _nat_cmp(rc1, rc2)
|
||||
|
||||
if not rccmp:
|
||||
return 0
|
||||
if not rc1:
|
||||
return 1
|
||||
elif not rc2:
|
||||
return -1
|
||||
|
||||
return rccmp
|
||||
|
||||
|
||||
def compare(ver1, ver2):
|
||||
"""Compare two versions
|
||||
|
||||
:param ver1: version string 1
|
||||
:param ver2: version string 2
|
||||
:return: The return value is negative if ver1 < ver2,
|
||||
zero if ver1 == ver2 and strictly positive if ver1 > ver2
|
||||
:rtype: int
|
||||
"""
|
||||
|
||||
v1, v2 = parse(ver1), parse(ver2)
|
||||
|
||||
return _compare_by_keys(v1, v2)
|
||||
|
||||
|
||||
def match(version, match_expr):
|
||||
"""Compare two versions through a comparison
|
||||
|
||||
:param str version: a version string
|
||||
:param str match_expr: operator and version; valid operators are
|
||||
< smaller than
|
||||
> greater than
|
||||
>= greator or equal than
|
||||
<= smaller or equal than
|
||||
== equal
|
||||
!= not equal
|
||||
:return: True if the expression matches the version, otherwise False
|
||||
:rtype: bool
|
||||
"""
|
||||
prefix = match_expr[:2]
|
||||
if prefix in ('>=', '<=', '==', '!='):
|
||||
match_version = match_expr[2:]
|
||||
elif prefix and prefix[0] in ('>', '<'):
|
||||
prefix = prefix[0]
|
||||
match_version = match_expr[1:]
|
||||
else:
|
||||
raise ValueError("match_expr parameter should be in format <op><ver>, "
|
||||
"where <op> is one of "
|
||||
"['<', '>', '==', '<=', '>=', '!=']. "
|
||||
"You provided: %r" % match_expr)
|
||||
|
||||
possibilities_dict = {
|
||||
'>': (1,),
|
||||
'<': (-1,),
|
||||
'==': (0,),
|
||||
'!=': (-1, 1),
|
||||
'>=': (0, 1),
|
||||
'<=': (-1, 0)
|
||||
}
|
||||
|
||||
possibilities = possibilities_dict[prefix]
|
||||
cmp_res = compare(version, match_version)
|
||||
|
||||
return cmp_res in possibilities
|
||||
|
||||
|
||||
def max_ver(ver1, ver2):
|
||||
"""Returns the greater version of two versions
|
||||
|
||||
:param ver1: version string 1
|
||||
:param ver2: version string 2
|
||||
:return: the greater version of the two
|
||||
:rtype: :class:`VersionInfo`
|
||||
"""
|
||||
cmp_res = compare(ver1, ver2)
|
||||
if cmp_res == 0 or cmp_res == 1:
|
||||
return ver1
|
||||
else:
|
||||
return ver2
|
||||
|
||||
|
||||
def min_ver(ver1, ver2):
|
||||
"""Returns the smaller version of two versions
|
||||
|
||||
:param ver1: version string 1
|
||||
:param ver2: version string 2
|
||||
:return: the smaller version of the two
|
||||
:rtype: :class:`VersionInfo`
|
||||
"""
|
||||
cmp_res = compare(ver1, ver2)
|
||||
if cmp_res == 0 or cmp_res == -1:
|
||||
return ver1
|
||||
else:
|
||||
return ver2
|
||||
|
||||
|
||||
def format_version(major, minor, patch, prerelease=None, build=None):
|
||||
"""Format a version according to the Semantic Versioning specification
|
||||
|
||||
:param str major: the required major part of a version
|
||||
:param str minor: the required minor part of a version
|
||||
:param str patch: the required patch part of a version
|
||||
:param str prerelease: the optional prerelease part of a version
|
||||
:param str build: the optional build part of a version
|
||||
:return: the formatted string
|
||||
:rtype: str
|
||||
"""
|
||||
version = "%d.%d.%d" % (major, minor, patch)
|
||||
if prerelease is not None:
|
||||
version = version + "-%s" % prerelease
|
||||
|
||||
if build is not None:
|
||||
version = version + "+%s" % build
|
||||
|
||||
return version
|
||||
|
||||
|
||||
def _increment_string(string):
|
||||
"""
|
||||
Look for the last sequence of number(s) in a string and increment, from:
|
||||
http://code.activestate.com/recipes/442460-increment-numbers-in-a-string/#c1
|
||||
"""
|
||||
match = _LAST_NUMBER.search(string)
|
||||
if match:
|
||||
next_ = str(int(match.group(1)) + 1)
|
||||
start, end = match.span(1)
|
||||
string = string[:max(end - len(next_), start)] + next_ + string[end:]
|
||||
return string
|
||||
|
||||
|
||||
def bump_major(version):
|
||||
"""Raise the major part of the version
|
||||
|
||||
:param: version string
|
||||
:return: the raised version string
|
||||
:rtype: str
|
||||
"""
|
||||
verinfo = parse(version)
|
||||
return format_version(verinfo['major'] + 1, 0, 0)
|
||||
|
||||
|
||||
def bump_minor(version):
|
||||
"""Raise the minor part of the version
|
||||
|
||||
:param: version string
|
||||
:return: the raised version string
|
||||
:rtype: str
|
||||
"""
|
||||
verinfo = parse(version)
|
||||
return format_version(verinfo['major'], verinfo['minor'] + 1, 0)
|
||||
|
||||
|
||||
def bump_patch(version):
|
||||
"""Raise the patch part of the version
|
||||
|
||||
:param: version string
|
||||
:return: the raised version string
|
||||
:rtype: str
|
||||
"""
|
||||
verinfo = parse(version)
|
||||
return format_version(verinfo['major'], verinfo['minor'],
|
||||
verinfo['patch'] + 1)
|
||||
|
||||
|
||||
def bump_prerelease(version, token='rc'):
|
||||
"""Raise the prerelease part of the version
|
||||
|
||||
:param version: version string
|
||||
:param token: defaults to 'rc'
|
||||
:return: the raised version string
|
||||
:rtype: str
|
||||
"""
|
||||
verinfo = parse(version)
|
||||
verinfo['prerelease'] = _increment_string(
|
||||
verinfo['prerelease'] or (token or 'rc') + '.0'
|
||||
)
|
||||
return format_version(verinfo['major'], verinfo['minor'], verinfo['patch'],
|
||||
verinfo['prerelease'])
|
||||
|
||||
|
||||
def bump_build(version, token='build'):
|
||||
"""Raise the build part of the version
|
||||
|
||||
:param version: version string
|
||||
:param token: defaults to 'build'
|
||||
:return: the raised version string
|
||||
:rtype: str
|
||||
"""
|
||||
verinfo = parse(version)
|
||||
verinfo['build'] = _increment_string(
|
||||
verinfo['build'] or (token or 'build') + '.0'
|
||||
)
|
||||
return format_version(verinfo['major'], verinfo['minor'], verinfo['patch'],
|
||||
verinfo['prerelease'], verinfo['build'])
|
||||
@@ -27,7 +27,6 @@ required = [
|
||||
'pip>=9.0.1',
|
||||
'pip-tools>=1.9.0',
|
||||
'setuptools>=36.3.0',
|
||||
'semver',
|
||||
'requests>2.0.0'
|
||||
]
|
||||
|
||||
@@ -36,7 +35,7 @@ if sys.version_info < (2, 7):
|
||||
required.append('ordereddict')
|
||||
|
||||
|
||||
class PublishCommand(Command):
|
||||
class UploadCommand(Command):
|
||||
"""Support setup.py publish."""
|
||||
|
||||
description = 'Build and publish the package.'
|
||||
@@ -60,7 +59,7 @@ class PublishCommand(Command):
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
self.status('Building Source and Wheel (universal) distribution…')
|
||||
self.status('Building Source distribution…')
|
||||
os.system('{0} setup.py sdist'.format(sys.executable))
|
||||
|
||||
self.status('Uploading the package to PyPi via Twine…')
|
||||
@@ -96,4 +95,7 @@ setup(
|
||||
'Programming Language :: Python :: Implementation :: CPython',
|
||||
'Programming Language :: Python :: Implementation :: PyPy'
|
||||
],
|
||||
cmdclass={
|
||||
'upload': UploadCommand,
|
||||
},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user