From afda4a9c900f522fd1516d3fb1a5641280036f48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sun, 24 Apr 2016 23:03:18 +0200 Subject: [PATCH] [enh] Implement the intersection of package version Specifier class --- src/yunohost/utils/packages.py | 99 ++++++++++++++++++++++++++++------ 1 file changed, 82 insertions(+), 17 deletions(-) diff --git a/src/yunohost/utils/packages.py b/src/yunohost/utils/packages.py index edade553f..82ebed243 100644 --- a/src/yunohost/utils/packages.py +++ b/src/yunohost/utils/packages.py @@ -98,14 +98,19 @@ class Specifier(object): } def __init__(self, spec): - match = self._regex.search(spec) - if not match: - raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec)) + if isinstance(spec, basestring): + match = self._regex.search(spec) + if not match: + raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec)) - self._spec = ( - match.group("relation").strip(), - match.group("version").strip(), - ) + self._spec = ( + match.group("relation").strip(), + match.group("version").strip(), + ) + elif isinstance(spec, self.__class__): + self._spec = spec._spec + else: + return NotImplemented def __repr__(self): return "".format(str(self)) @@ -138,6 +143,9 @@ class Specifier(object): return self._spec != other._spec + def __and__(self, other): + return self.intersection(other) + def _get_relation(self, op): return getattr(self, "_compare_{0}".format(self._relations[op])) @@ -167,6 +175,49 @@ class Specifier(object): def __contains__(self, item): return self.contains(item) + def intersection(self, other): + """Make the intersection of two specifiers + + Return a new `SpecifierSet` with version specifier(s) common to the + specifier and the other. + + Example: + >>> Specifier('>= 2.2') & '>> 2.2.1' == '>> 2.2.1' + >>> Specifier('>= 2.2') & '<< 2.3' == '>= 2.2, << 2.3' + + """ + if isinstance(other, basestring): + try: + other = self.__class__(other) + except InvalidSpecifier: + return NotImplemented + elif not isinstance(other, self.__class__): + return NotImplemented + + # store spec parts for easy access + rel1, v1 = self.relation, self.version + rel2, v2 = other.relation, other.version + result = [] + + if other == self: + result = [other] + elif rel1 == '=': + result = [self] if v1 in other else None + elif rel2 == '=': + result = [other] if v2 in self else None + elif v1 == v2: + result = [other if rel1[1] == '=' else self] + elif v2 in self or v1 in other: + is_self_greater = version_compare(v1, v2) > 0 + if rel1[0] == rel2[0]: + if rel1[0] == '>': + result = [self if is_self_greater else other] + else: + result = [other if is_self_greater else self] + else: + result = [self, other] + return SpecifierSet(result if result is not None else '') + def contains(self, item): return self._get_relation(self.relation)(item, self.version) @@ -181,7 +232,9 @@ class SpecifierSet(object): """ def __init__(self, specifiers): - specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] + if isinstance(specifiers, basestring): + specifiers = [s.strip() for s in specifiers.split(",") + if s.strip()] parsed = set() for specifier in specifiers: @@ -198,15 +251,8 @@ class SpecifierSet(object): def __hash__(self): return hash(self._specs) - def __and__(self, other): - if isinstance(other, basestring): - other = SpecifierSet(other) - elif not isinstance(other, SpecifierSet): - return NotImplemented - - specifier = SpecifierSet() - specifier._specs = frozenset(self._specs | other._specs) - return specifiers + def __or__(self, other): + return self.union(other) def __eq__(self, other): if isinstance(other, basestring): @@ -237,6 +283,25 @@ class SpecifierSet(object): def __contains__(self, item): return self.contains(item) + def union(self, other): + """Make the union of two specifiers set + + Return a new `SpecifierSet` with version specifiers from the set + and the other. + + Example: + >>> SpecifierSet('>= 2.2') | '<< 2.3' == '>= 2.2, << 2.3' + + """ + if isinstance(other, basestring): + other = SpecifierSet(other) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + specifiers = SpecifierSet([]) + specifiers._specs = frozenset(self._specs | other._specs) + return specifiers + def contains(self, item): return all( s.contains(item)