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
parent 51dbafacc637
Pipeline #66947 passed with stages
in 43 minutes and 18 seconds
......@@ -509,10 +509,17 @@ def max_version(a, b):
def split_constraint(constraint):
oper = itertools.takewhile(lambda x: x in "<>=", constraint)
version = itertools.dropwhile(lambda x: x not in string.digits + ".", constraint)
return "".join(oper), "".join(version)
def parse_constraints(stream):
constraints = []
for block in stream.split(","):
block = block.strip()
return constraints
class ConfigurationProblem:
"""Each cube has its own list of dependencies on other cubes/versions.
......@@ -545,12 +552,12 @@ class ConfigurationProblem:
] = self.config.cube_depends_cubicweb_version(cube)
# compute reverse dependencies
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())
if constraint:
if constraints:
oper, version = split_constraint(constraint)
self.reverse_dependencies[name].add((oper, version, cube))
for oper, version in parse_constraints(constraints):
self.reverse_dependencies[name].add((oper, version, cube))
except Exception:
"cube %s depends on %s but constraint badly "
......@@ -560,36 +567,30 @@ class ConfigurationProblem:
self.reverse_dependencies[name].add((None, None, cube))
# check consistency
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:
"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:
self.errors.append(("add", cube, version, source))
elif versions:
lower_strict = version_strictly_lower(self.cubes[cube], version)
if oper in (">=", "=", "=="):
if lower_strict:
self.errors.append(("update", cube, version, source))
elif oper is None:
pass # no constraint on version
self.errors.append(("add", cube, min_version, source))
min_version, min_source = None, None
max_version, max_source = None, None
for oper, version, source in versions:
if oper == ">=":
if version_strictly_lower(min_version, 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:
print("unknown operator", oper)
"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))
Supports Markdown
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