137 lines
4.6 KiB
Python
137 lines
4.6 KiB
Python
# -*- coding=utf-8 -*-
|
|
|
|
from __future__ import absolute_import, unicode_literals
|
|
|
|
import itertools
|
|
import operator
|
|
|
|
from packaging.specifiers import SpecifierSet, Specifier
|
|
from vistir.misc import dedup
|
|
|
|
|
|
def _tuplize_version(version):
|
|
return tuple(int(x) for x in version.split("."))
|
|
|
|
|
|
def _format_version(version):
|
|
return ".".join(str(i) for i in version)
|
|
|
|
|
|
# Prefer [x,y) ranges.
|
|
REPLACE_RANGES = {">": ">=", "<=": "<"}
|
|
|
|
|
|
def _format_pyspec(specifier):
|
|
if isinstance(specifier, str):
|
|
if not any(op in specifier for op in Specifier._operators.keys()):
|
|
specifier = "=={0}".format(specifier)
|
|
specifier = Specifier(specifier)
|
|
if specifier.operator == "==" and specifier.version.endswith(".*"):
|
|
specifier = Specifier("=={0}".format(specifier.version[:-2]))
|
|
try:
|
|
op = REPLACE_RANGES[specifier.operator]
|
|
except KeyError:
|
|
return specifier
|
|
version = specifier.version.replace(".*", "")
|
|
curr_tuple = _tuplize_version(version)
|
|
try:
|
|
next_tuple = (curr_tuple[0], curr_tuple[1] + 1)
|
|
except IndexError:
|
|
next_tuple = (curr_tuple[0], 1)
|
|
specifier = Specifier("{0}{1}".format(op, _format_version(next_tuple)))
|
|
return specifier
|
|
|
|
|
|
def _get_specs(specset):
|
|
if isinstance(specset, Specifier):
|
|
specset = str(specset)
|
|
if isinstance(specset, str):
|
|
specset = SpecifierSet(specset.replace(".*", ""))
|
|
return [
|
|
(spec._spec[0], _tuplize_version(spec._spec[1]))
|
|
for spec in getattr(specset, "_specs", [])
|
|
]
|
|
|
|
|
|
def _group_by_op(specs):
|
|
specs = [_get_specs(x) for x in list(specs)]
|
|
flattened = [(op, version) for spec in specs for op, version in spec]
|
|
specs = sorted(flattened, key=operator.itemgetter(1))
|
|
grouping = itertools.groupby(specs, key=operator.itemgetter(0))
|
|
return grouping
|
|
|
|
|
|
def cleanup_pyspecs(specs, joiner="or"):
|
|
specs = {_format_pyspec(spec) for spec in specs}
|
|
# for != operator we want to group by version
|
|
# if all are consecutive, join as a list
|
|
results = set()
|
|
for op, versions in _group_by_op(specs):
|
|
versions = [version[1] for version in versions]
|
|
versions = sorted(dedup(versions))
|
|
# if we are doing an or operation, we need to use the min for >=
|
|
# this way OR(>=2.6, >=2.7, >=3.6) picks >=2.6
|
|
# if we do an AND operation we need to use MAX to be more selective
|
|
if op in (">", ">="):
|
|
if joiner == "or":
|
|
results.add((op, _format_version(min(versions))))
|
|
else:
|
|
results.add((op, _format_version(max(versions))))
|
|
# we use inverse logic here so we will take the max value if we are
|
|
# using OR but the min value if we are using AND
|
|
elif op in ("<=", "<"):
|
|
if joiner == "or":
|
|
results.add((op, _format_version(max(versions))))
|
|
else:
|
|
results.add((op, _format_version(min(versions))))
|
|
# leave these the same no matter what operator we use
|
|
elif op in ("!=", "==", "~="):
|
|
version_list = sorted(
|
|
"{0}".format(_format_version(version))
|
|
for version in versions
|
|
)
|
|
version = ", ".join(version_list)
|
|
if len(version_list) == 1:
|
|
results.add((op, version))
|
|
elif op == "!=":
|
|
results.add(("not in", version))
|
|
elif op == "==":
|
|
results.add(("in", version))
|
|
else:
|
|
specifier = SpecifierSet(",".join(sorted(
|
|
"{0}".format(op, v) for v in version_list
|
|
)))._specs
|
|
for s in specifier:
|
|
results &= (specifier._spec[0], specifier._spec[1])
|
|
else:
|
|
if len(version) == 1:
|
|
results.add((op, version))
|
|
else:
|
|
specifier = SpecifierSet("{0}".format(version))._specs
|
|
for s in specifier:
|
|
results |= (specifier._spec[0], specifier._spec[1])
|
|
return results
|
|
|
|
|
|
def pyspec_from_markers(marker):
|
|
if marker._markers[0][0] != 'python_version':
|
|
return
|
|
op = marker._markers[0][1].value
|
|
version = marker._markers[0][2].value
|
|
specset = set()
|
|
if op == "in":
|
|
specset.update(
|
|
Specifier("=={0}".format(v.strip()))
|
|
for v in version.split(",")
|
|
)
|
|
elif op == "not in":
|
|
specset.update(
|
|
Specifier("!={0}".format(v.strip()))
|
|
for v in version.split(",")
|
|
)
|
|
else:
|
|
specset.add(Specifier("".join([op, version])))
|
|
if specset:
|
|
return specset
|
|
return None
|