Source code for pkgcore.ebuild.restricts

# "More than one statement on a single line"
# pylint: disable-msg=C0321

"""
atom version restrict
"""

__all__ = (
    "CategoryDep",
    "PackageDep",
    "RepositoryDep",
    "SlotDep",
    "StaticUseDep",
    "SubSlotDep",
    "UseDepDefault",
    "VersionMatch",
)

from snakeoil.klass import generic_equality

from ..restrictions import packages, restriction, values
from . import cpv, errors


# TODO: change values.EqualityMatch so it supports le, lt, gt, ge, eq,
# ne ops, and convert this to it.
class _VersionMatch(restriction.base, metaclass=generic_equality):
    """
    package restriction implementing gentoo ebuild version comparison rules

    any overriding of this class *must* maintain numerical order of
    self.vals, see intersect for reason why. vals also must be a tuple.
    """

    __slots__ = ("ver", "rev", "vals", "droprev", "negate")

    __attr_comparison__ = ("negate", "rev", "droprev", "vals")

    type = restriction.value_type
    attr = "fullver"

    _convert_op2str = {
        (-1,): "<",
        (-1, 0): "<=",
        (0,): "=",
        (0, 1): ">=",
        (1,): ">",
    }

    _convert_str2op = {v: k for k, v in _convert_op2str.items()}

    def __init__(self, operator, ver, rev=None, negate=False, **kwd):
        """
        :param operator: version comparison to do,
            valid operators are ('<', '<=', '=', '>=', '>', '~')
        :type operator: string
        :param ver: version to base comparison on
        :type ver: string
        :param rev: revision to base comparison on
        :type rev: None (no rev), or an int
        :param negate: should the restriction results be negated;
            currently forced to False
        """

        kwd["negate"] = False
        sf = object.__setattr__
        sf(self, "ver", ver)
        sf(self, "rev", rev)
        if operator != "~" and operator not in self._convert_str2op:
            raise errors.InvalidVersion(
                self.ver, self.rev, f"invalid operator, '{operator}'"
            )

        sf(self, "negate", negate)
        if operator == "~":
            if ver is None:
                raise ValueError("for ~ op, version must be something other then None")
            sf(self, "droprev", True)
            sf(self, "vals", (0,))
        else:
            sf(self, "droprev", False)
            sf(self, "vals", self._convert_str2op[operator])

    def match(self, pkg, *args, **kwargs):
        if self.droprev:
            r1, r2 = None, None
        else:
            r1, r2 = self.rev, pkg.revision

        if pkg.version is None:
            return False

        return (cpv.ver_cmp(pkg.version, r2, self.ver, r1) in self.vals) != self.negate

    def __str__(self):
        s = self._convert_op2str[self.vals]

        if self.negate:
            n = "not "
        else:
            n = ""

        if self.droprev or not self.rev:
            return f"ver {n}{s} {self.ver}"
        return f"ver-rev {n}{s} {self.ver}-r{self.rev}"

    def __repr__(self):
        s = self._convert_op2str[self.vals]
        s += self.ver
        if self.rev:
            s += f"-r{self.rev}"
        return "<%s %s negate=%s droprrev=%s @#x>" % (
            self.__class__.__name__,
            s,
            self.negate,
            self.droprev,
        )

    @staticmethod
    def _convert_ops(inst):
        if inst.negate:
            if inst.droprev:
                return inst.vals
            return tuple(sorted(set((-1, 0, 1)).difference(inst.vals)))
        return inst.vals

    def __eq__(self, other):
        if self is other:
            return True
        if isinstance(other, self.__class__):
            if (
                self.droprev != other.droprev
                or self.ver != other.ver
                or self.rev != other.rev
            ):
                return False
            return self._convert_ops(self) == self._convert_ops(other)

        return False

    def __hash__(self):
        return hash((self.droprev, self.ver, self.rev, self.negate, self.vals))


[docs] class VersionMatch(packages.PackageRestriction): __slots__ = () __inst_caching__ = True def __init__(self, *args, **kwds): v = _VersionMatch(*args, **kwds) super().__init__("fullver", v, negate=kwds.get("negate", False))
[docs] def match(self, pkg, *args, **kwds): return self.restriction.match(pkg)
[docs] class SlotDep(packages.PackageRestriction): __slots__ = () __inst_caching__ = True def __init__(self, slot, **kwds): v = values.StrExactMatch(slot) super().__init__("slot", v, negate=kwds.get("negate", False))
[docs] class SubSlotDep(packages.PackageRestriction): __slots__ = () __inst_caching__ = True def __init__(self, subslot, **kwds): v = values.StrExactMatch(subslot) super().__init__("subslot", v, negate=kwds.get("negate", False))
[docs] class CategoryDep(packages.PackageRestriction): __slots__ = () __inst_caching__ = True def __init__(self, category, negate=False): super().__init__("category", values.StrExactMatch(category, negate=negate))
[docs] class PackageDep(packages.PackageRestriction): __slots__ = () __inst_caching__ = True def __init__(self, package, negate=False): super().__init__("package", values.StrExactMatch(package, negate=negate))
[docs] class RepositoryDep(packages.PackageRestriction): __slots__ = () __inst_caching__ = True def __init__(self, repo_id, negate=False): super().__init__("repo.repo_id", values.StrExactMatch(repo_id), negate=negate)
[docs] class StaticUseDep(packages.PackageRestriction): __slots__ = () __inst_caching__ = True def __init__(self, false_use, true_use): v = [] if false_use: v.append(values.ContainmentMatch(false_use, negate=True, match_all=True)) if true_use: v.append(values.ContainmentMatch(true_use, match_all=True)) l = len(v) if l == 2: v = values.AndRestriction(*v) elif l == 1: v = v[0] else: v = values.AlwaysTrue super().__init__("use", v)
class _UseDepDefaultContainment(values.ContainmentMatch): __slots__ = ("if_missing",) def __init__(self, if_missing, vals, negate=False): object.__setattr__(self, "if_missing", bool(if_missing)) super().__init__(vals, negate=negate, match_all=True) def match(self, val): reduced_vals = self.vals iuse_stripped, use = val if reduced_vals.issubset(iuse_stripped): # use normal pathways. return values.ContainmentMatch.match(self, use) if self.if_missing == self.negate: # ex: if is_missing = False, missing flags are assumed falsed. # if negate is False, then we're not trying to disable the flags, trying to enable. # as such, this cannot match. The inverse holds true. return False # to reach here, either we're trying to force all flags false and the default is False, # or we're trying to force all flags on, and the default is on. # recall that negate is unfortunately a double negative in labeling... reduced_vals = reduced_vals.intersection(iuse_stripped) if reduced_vals: return values.ContainmentMatch.match( self, use, _values_override=reduced_vals ) # nothing to match means all are missing, but the default makes them considered a match. return True def force_False(self, pkg, attr, val): reduced_vals = self.vals # see comments in .match for clarification of logic. iuse_stripped, use = val if reduced_vals.issubset(iuse_stripped): return values.ContainmentMatch.force_False(self, pkg, "use", use) if self.if_missing == self.negate: return False reduced_vals = reduced_vals.intersection(iuse_stripped) if reduced_vals: return values.ContainmentMatch.force_False( self, pkg, "use", use, reduced_vals ) return True def force_True(self, pkg, attr, val): reduced_vals = self.vals # see comments in .match for clarification of logic. iuse_stripped, use = val if reduced_vals.issubset(iuse_stripped): return values.ContainmentMatch.force_True(self, pkg, "use", use) if self.if_missing == self.negate: return False reduced_vals = reduced_vals.intersection(iuse_stripped) if reduced_vals: return values.ContainmentMatch.force_True( self, pkg, "use", use, reduced_vals ) return True
[docs] class UseDepDefault(packages.PackageRestrictionMulti): __slots__ = () __inst_caching__ = True def __init__(self, if_missing, false_use, true_use): v = [] if false_use: v.append(_UseDepDefaultContainment(if_missing, false_use, negate=True)) if true_use: v.append(_UseDepDefaultContainment(if_missing, true_use)) l = len(v) if l == 2: v = values.AndRestriction(*v) elif l == 1: v = v[0] else: v = values.AlwaysTrue super().__init__(("iuse_stripped", "use"), v)
def _parse_nontransitive_use(sequence): default_off = [[], []] default_on = [[], []] normal = [[], []] for token in sequence: if token[-1] == ")": if token[-2] == "+": trg = default_on else: trg = default_off token = token[:-3] else: trg = normal if token[0] == "-": trg[0].append(token[1:]) else: trg[1].append(token) r = [] default_off = (tuple(default_off[0]), tuple(default_off[1])) default_on = (tuple(default_on[0]), tuple(default_on[1])) normal = (tuple(normal[0]), tuple(normal[1])) if normal[0] or normal[1]: r.append(StaticUseDep(*normal)) if default_off[0] or default_off[1]: r.append(UseDepDefault(False, *default_off)) if default_on[0] or default_on[1]: r.append(UseDepDefault(True, *default_on)) return r