1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
import collections
import json
import operator
import os
import pytest
from resolvelib import AbstractProvider, Resolver
Requirement = collections.namedtuple("Requirement", "container constraint")
Candidate = collections.namedtuple("Candidate", "container version")
INPUTS_DIR = os.path.abspath(os.path.join(__file__, "..", "inputs"))
INPUT_NAMES = [n for n in os.listdir(INPUTS_DIR) if n.endswith(".json")]
def _parse_version(s):
major, minor, rest = s.split(".", 2)
if "-" in rest:
patch, rest = rest.split("-", 1)
else:
patch, rest = rest, ""
return (int(major), int(minor), int(patch), rest)
def _is_version_allowed(version, ranges):
"""Check version compatibility with Sematic Versioning."""
for r in ranges:
r = _parse_version(r)
if r[0] != version[0]:
continue
if r[0] == 0:
if version[:2] == r[:2] and version[2] >= r[2]:
return True
else:
if version[1:] >= r[1:]:
return True
return False
def _calculate_preference(parsed_version):
"""Calculate preference of a version with Minimal Version Selection."""
if parsed_version[0] == 0:
return (
0,
-parsed_version[1],
parsed_version[2],
-len(parsed_version[3][:1]),
parsed_version[3],
)
return (
-parsed_version[0],
parsed_version[1],
parsed_version[2],
-len(parsed_version[3][:1]),
parsed_version[3],
)
class SwiftInputProvider(AbstractProvider):
def __init__(self, filename):
with open(filename) as f:
input_data = json.load(f)
self.containers = {
container["identifier"]: container for container in input_data["containers"]
}
self.root_requirements = [
Requirement(self.containers[constraint["identifier"]], constraint)
for constraint in input_data["constraints"]
]
self.expectation = input_data["result"]
def identify(self, requirement_or_candidate):
return requirement_or_candidate.container["identifier"]
def get_preference(
self,
identifier,
resolutions,
candidates,
information,
backtrack_causes,
):
return sum(1 for _ in candidates[identifier])
def _iter_matches(self, identifier, requirements, incompatibilities):
bad_versions = {c.version for c in incompatibilities[identifier]}
container = next(requirements[identifier]).container
for version in container["versions"]:
if version in bad_versions:
continue
ver = _parse_version(version)
satisfied = all(
_is_version_allowed(ver, r.constraint["requirement"])
for r in requirements[identifier]
)
if not satisfied:
continue
preference = _calculate_preference(ver)
yield (preference, Candidate(container, version))
def find_matches(self, identifier, requirements, incompatibilities):
matches = sorted(
self._iter_matches(identifier, requirements, incompatibilities),
key=operator.itemgetter(0),
reverse=True,
)
for _, candidate in matches:
yield candidate
def is_satisfied_by(self, requirement, candidate):
return _is_version_allowed(
_parse_version(candidate.version),
requirement.constraint["requirement"],
)
def _iter_dependencies(self, candidate):
for constraint in candidate.container["versions"][candidate.version]:
identifier = constraint["identifier"]
try:
container = self.containers[identifier]
except KeyError:
# Package does not exist. Return a stub without candidates.
container = {"identifier": identifier, "versions": {}}
yield Requirement(container, constraint)
def get_dependencies(self, candidate):
return list(self._iter_dependencies(candidate))
@pytest.fixture(
params=[os.path.join(INPUTS_DIR, n) for n in INPUT_NAMES],
ids=[n[:-5] for n in INPUT_NAMES],
)
def provider(request):
return SwiftInputProvider(request.param)
def test_resolver(provider, reporter):
resolver = Resolver(provider, reporter)
result = resolver.resolve(provider.root_requirements)
display = {
identifier: candidate.version
for identifier, candidate in result.mapping.items()
}
assert display == provider.expectation
|