Commit 4bafcd39 authored by Nicolas Chauvat's avatar Nicolas Chauvat
Browse files

feat: improve migration.ConfigurationProblem.solve()

Previously the code could only handle single constraints when expressing
dependencies in __pkginfo__ as in:

  "blog": ">= 1.1.0",

This improves on the previous situation by handling cases where
multiple constraints are stated as in:

  "blog": ">= 1.1.0, < 1.2.0",

Since we have been switched (or have switched) to using requirements.txt
and pip for installing packages, maybe this whole check could be removed
entirely.
parent 51dbafacc637
Pipeline #66947 passed with stages
in 43 minutes and 18 seconds
...@@ -509,10 +509,17 @@ def max_version(a, b): ...@@ -509,10 +509,17 @@ def max_version(a, b):
def split_constraint(constraint): def split_constraint(constraint):
oper = itertools.takewhile(lambda x: x in "<>=", constraint) oper = itertools.takewhile(lambda x: x in "<>=", constraint)
version = itertools.dropwhile(lambda x: x not in string.digits + ".", constraint) version = itertools.dropwhile(lambda x: x not in string.digits + ".", constraint)
return "".join(oper), "".join(version) return "".join(oper), "".join(version)
def parse_constraints(stream):
constraints = []
for block in stream.split(","):
block = block.strip()
constraints.append(split_constraint(block))
return constraints
class ConfigurationProblem: class ConfigurationProblem:
"""Each cube has its own list of dependencies on other cubes/versions. """Each cube has its own list of dependencies on other cubes/versions.
...@@ -545,12 +552,12 @@ class ConfigurationProblem: ...@@ -545,12 +552,12 @@ class ConfigurationProblem:
] = self.config.cube_depends_cubicweb_version(cube) ] = self.config.cube_depends_cubicweb_version(cube)
# compute reverse dependencies # compute reverse dependencies
for cube, dependencies in self.dependencies.items(): for cube, dependencies in self.dependencies.items():
for name, constraint in dependencies.items(): for name, constraints in dependencies.items():
self.reverse_dependencies.setdefault(name, set()) self.reverse_dependencies.setdefault(name, set())
if constraint: if constraints:
try: try:
oper, version = split_constraint(constraint) for oper, version in parse_constraints(constraints):
self.reverse_dependencies[name].add((oper, version, cube)) self.reverse_dependencies[name].add((oper, version, cube))
except Exception: except Exception:
self.warnings.append( self.warnings.append(
"cube %s depends on %s but constraint badly " "cube %s depends on %s but constraint badly "
...@@ -560,36 +567,30 @@ class ConfigurationProblem: ...@@ -560,36 +567,30 @@ class ConfigurationProblem:
self.reverse_dependencies[name].add((None, None, cube)) self.reverse_dependencies[name].add((None, None, cube))
# check consistency # check consistency
for cube, versions in sorted(self.reverse_dependencies.items()): for cube, versions in sorted(self.reverse_dependencies.items()):
oper, version, source = None, None, None
# simplify constraints
if versions:
for constraint in versions:
op, ver, src = constraint
if oper is None:
oper = op
version = ver
source = src
elif op == ">=" and oper == ">=":
if version_strictly_lower(version, ver):
version = ver
source = src
elif op == None:
continue
else:
print(
"unable to handle %s in %s, set to `%s %s` "
"but currently up to `%s %s`"
% (cube, source, oper, version, op, ver)
)
# "solve" constraint satisfaction problem
if cube not in self.cubes: if cube not in self.cubes:
self.errors.append(("add", cube, version, source)) self.errors.append(("add", cube, min_version, source))
elif versions: continue
lower_strict = version_strictly_lower(self.cubes[cube], version) min_version, min_source = None, None
if oper in (">=", "=", "=="): max_version, max_source = None, None
if lower_strict: for oper, version, source in versions:
self.errors.append(("update", cube, version, source)) if oper == ">=":
elif oper is None: if version_strictly_lower(min_version, version):
pass # no constraint on version min_version = version
min_source = source
elif oper == "<":
if version_strictly_lower(version, max_version):
max_version = version
max_source = source
elif oper == None:
pass
else: else:
print("unknown operator", oper) print(
"unable to handle %s in %s, set to `%s %s` "
"but currently up to `%s %s`"
% (cube, source, oper, version, min_version, max_version)
)
installed_version = self.cubes[cube]
if min_version and version_strictly_lower(installed_version, min_version):
self.errors.append(("update", cube, min_version, min_source))
if max_version and version_strictly_lower(max_version, installed_version):
self.errors.append(("update", cube, max_version, max_source))
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment