Ejemplo n.º 1
0
    def _identify_candidates(self, restrict, sorter):
        # full expansion
        if not isinstance(restrict, boolean.base) or isinstance(restrict, atom):
            return self._fast_identify_candidates(restrict, sorter)
        dsolutions = [
            ([c.restriction
              for c in collect_package_restrictions(x, ("category",))],
             [p.restriction
              for p in collect_package_restrictions(x, ("package",))])
            for x in restrict.iter_dnf_solutions(True)]

        # see if any solution state isn't dependent on cat/pkg in anyway.
        # if so, search whole search space.
        for x in dsolutions:
            if not x[0] and not x[1]:
                if sorter is iter:
                    return self.versions
                return (
                    (c, p)
                    for c in sorter(self.categories)
                    for p in sorter(self.packages.get(c, ())))

        # simple cases first.
        # if one specifies categories, and one doesn't
        cat_specified = bool(dsolutions[0][0])
        pkg_specified = bool(dsolutions[0][1])
        pgetter = self.packages.get
        if any(True for x in dsolutions[1:] if bool(x[0]) != cat_specified):
            if any(True for x in dsolutions[1:] if bool(x[1]) != pkg_specified):
                # merde.  so we've got a mix- some specify cats, some
                # don't, some specify pkgs, some don't.
                # this may be optimizable
                return self.versions
            # ok. so... one doesn't specify a category, but they all
            # specify packages (or don't)
            pr = values.OrRestriction(
                *tuple(iflatten_instance(
                    (x[1] for x in dsolutions if x[1]), values.base)))
            return (
                (c, p)
                for c in sorter(self.categories)
                for p in sorter(pgetter(c, [])) if pr.match(p))

        elif any(True for x in dsolutions[1:] if bool(x[1]) != pkg_specified):
            # one (or more) don't specify pkgs, but they all specify cats.
            cr = values.OrRestriction(
                *tuple(iflatten_instance(
                    (x[0] for x in dsolutions), values.base)))
            cats_iter = (c for c in sorter(self.categories) if cr.match(c))
            return (
                (c, p)
                for c in cats_iter for p in sorter(pgetter(c, [])))

        return self._fast_identify_candidates(restrict, sorter)
Ejemplo n.º 2
0
    def _identify_candidates(self, restrict, sorter):
        # full expansion
        if not isinstance(restrict, boolean.base) or isinstance(restrict, atom):
            return self._fast_identify_candidates(restrict, sorter)
        dsolutions = [
            ([c.restriction
              for c in collect_package_restrictions(x, ("category",))],
             [p.restriction
              for p in collect_package_restrictions(x, ("package",))])
            for x in restrict.iter_dnf_solutions(True)]

        # see if any solution state isn't dependent on cat/pkg in anyway.
        # if so, search whole search space.
        for x in dsolutions:
            if not x[0] and not x[1]:
                if sorter is iter:
                    return self.versions
                return (
                    (c, p)
                    for c in sorter(self.categories)
                    for p in sorter(self.packages.get(c, ())))

        # simple cases first.
        # if one specifies categories, and one doesn't
        cat_specified = bool(dsolutions[0][0])
        pkg_specified = bool(dsolutions[0][1])
        pgetter = self.packages.get
        if any(True for x in dsolutions[1:] if bool(x[0]) != cat_specified):
            if any(True for x in dsolutions[1:] if bool(x[1]) != pkg_specified):
                # merde.  so we've got a mix- some specify cats, some
                # don't, some specify pkgs, some don't.
                # this may be optimizable
                return self.versions
            # ok. so... one doesn't specify a category, but they all
            # specify packages (or don't)
            pr = values.OrRestriction(
                *tuple(iflatten_instance(
                    (x[1] for x in dsolutions if x[1]), values.base)))
            return (
                (c, p)
                for c in sorter(self.categories)
                for p in sorter(pgetter(c, [])) if pr.match(p))

        elif any(True for x in dsolutions[1:] if bool(x[1]) != pkg_specified):
            # one (or more) don't specify pkgs, but they all specify cats.
            cr = values.OrRestriction(
                *tuple(iflatten_instance(
                    (x[0] for x in dsolutions), values.base)))
            cats_iter = (c for c in sorter(self.categories) if cr.match(c))
            return (
                (c, p)
                for c in cats_iter for p in sorter(pgetter(c, [])))

        return self._fast_identify_candidates(restrict, sorter)
Ejemplo n.º 3
0
    def test_collect_specific(self):
        prs = {}
        for x in ("category", "package", "version", "iuse"):
            prs[x] = packages.PackageRestriction(x, values.AlwaysTrue)

        r = packages.AndRestriction(packages.OrRestriction(*prs.values()),
                                    packages.AlwaysTrue)
        for k, v in prs.iteritems():
            self.assertEqual(
                list(util.collect_package_restrictions(r, attrs=[k])), [v])
        r = packages.AndRestriction(packages.OrRestriction(*prs.values()),
                                    *prs.values())
        for k, v in prs.iteritems():
            self.assertEqual(
                list(util.collect_package_restrictions(r, attrs=[k])), [v] * 2)
Ejemplo n.º 4
0
 def test_collect_all(self):
     prs = [packages.PackageRestriction("category", values.AlwaysTrue)] * 10
     self.assertEqual(
         list(util.collect_package_restrictions(packages.AndRestriction(
                     packages.OrRestriction(), packages.AndRestriction(),
                     *prs))),
         prs)
Ejemplo n.º 5
0
 def test_collect_all(self):
     prs = [packages.PackageRestriction("category", values.AlwaysTrue)] * 10
     self.assertEqual(
         list(util.collect_package_restrictions(packages.AndRestriction(
                     packages.OrRestriction(), packages.AndRestriction(),
                     *prs))),
         prs)
Ejemplo n.º 6
0
def _restrict_to_scope(restrict):
    """Determine a given restriction's scope level."""
    for scope, attrs in ((base.version_scope, ['fullver', 'version', 'rev']),
                         (base.package_scope,
                          ['package']), (base.category_scope, ['category'])):
        if any(collect_package_restrictions(restrict, attrs)):
            return scope
    return base.repo_scope
Ejemplo n.º 7
0
    def test_collect_specific(self):
        prs = {}
        for x in ("category", "package", "version", "iuse"):
            prs[x] = packages.PackageRestriction(x, values.AlwaysTrue)

        r = packages.AndRestriction(
            packages.OrRestriction(*prs.values()), packages.AlwaysTrue)
        for k, v in prs.iteritems():
            self.assertEqual(
                list(util.collect_package_restrictions(r, attrs=[k])),
                [v])
        r = packages.AndRestriction(packages.OrRestriction(
                *prs.values()), *prs.values())
        for k, v in prs.iteritems():
            self.assertEqual(
                list(util.collect_package_restrictions(r, attrs=[k])),
                [v] * 2)
Ejemplo n.º 8
0
def parse_match(text):
    """generate appropriate restriction for text

    Parsing basically breaks it down into chunks split by /, with each
    chunk allowing for prefix/postfix globbing- note that a postfixed
    glob on package token is treated as package attribute matching,
    not as necessarily a version match.

    If only one chunk is found, it's treated as a package chunk.
    Finally, it supports a nonstandard variation of atom syntax where
    the category can be dropped.

    Examples:

    - `*`: match all
    - `dev-*/*`: category must start with 'dev-'
    - `dev-*`: package must start with 'dev-'
    - `*-apps/portage*`: category must end in '-apps', package must start with
      'portage'
    - `>=portage-2.1`: atom syntax, package 'portage', version greater then or
      equal to '2.1'

    :param text: string to attempt to parse
    :type text: string
    :return: :obj:`pkgcore.restrictions.packages` derivative
    """

    # Ensure the text var is a string if we're under py3k.
    if not is_py3k:
        text = text.encode('ascii')
    orig_text = text = text.strip()
    if "!" in text:
        raise ParseError(
            "!, or any form of blockers make no sense in this usage: %s" % (
                text,))
    tsplit = text.rsplit("/", 1)
    if len(tsplit) == 1:
        ops, text = collect_ops(text)
        if not ops:
            if "*" in text:
                r = convert_glob(text)
                if r is None:
                    return packages.AlwaysTrue
                return packages.PackageRestriction("package", r)
        elif text.startswith("*"):
            raise ParseError(
                "cannot do prefix glob matches with version ops: %s" % (
                    orig_text,))
        # ok... fake category.  whee.
        try:
            r = list(util.collect_package_restrictions(
                    atom.atom("%scategory/%s" % (ops, text)).restrictions,
                    attrs=("category",), invert=True))
        except errors.MalformedAtom, e:
            raise_from(ParseError(str(e)))
        if len(r) == 1:
            return r[0]
        return packages.AndRestriction(*r)
Ejemplo n.º 9
0
    def _fast_identify_candidates(self, restrict, sorter):
        pkg_restrict = set()
        cat_restrict = set()
        cat_exact = set()
        pkg_exact = set()

        for x in collect_package_restrictions(restrict, ("category", "package")):
            if x.attr == "category":
                cat_restrict.add(x.restriction)
            elif x.attr == "package":
                pkg_restrict.add(x.restriction)

        for e, s in ((pkg_exact, pkg_restrict), (cat_exact, cat_restrict)):
            l = [x for x in s if isinstance(x, values.StrExactMatch) and not x.negate]
            s.difference_update(l)
            e.update(x.exact for x in l)
        del l

        if restrict.negate:
            cat_exact = pkg_exact = ()

        if cat_exact:
            if not cat_restrict and len(cat_exact) == 1:
                # Cannot use pop here, cat_exact is reused below.
                c = iter(cat_exact).next()
                if not pkg_restrict and len(pkg_exact) == 1:
                    cp = (c, pkg_exact.pop())
                    if cp in self.versions:
                        return [cp]
                    return []
                cats_iter = [c]
            else:
                cat_restrict.add(values.ContainmentMatch(*cat_exact))
                cats_iter = sorter(self._cat_filter(cat_restrict))
        elif cat_restrict:
            cats_iter = self._cat_filter(cat_restrict, negate=restrict.negate)
        else:
            cats_iter = sorter(self.categories)

        if pkg_exact:
            if not pkg_restrict:
                if sorter is iter:
                    pkg_exact = tuple(pkg_exact)
                else:
                    pkg_exact = sorter(pkg_exact)
                return ((c, p) for c in cats_iter for p in pkg_exact)
            else:
                pkg_restrict.add(values.ContainmentMatch(*pkg_exact))

        if pkg_restrict:
            return self._package_filter(cats_iter, pkg_restrict, negate=restrict.negate)
        elif not cat_restrict:
            if sorter is iter and not cat_exact:
                return self.versions
            else:
                return ((c, p) for c in cats_iter for p in sorter(self.packages.get(c, ())))
        return ((c, p) for c in cats_iter for p in sorter(self.packages.get(c, ())))
Ejemplo n.º 10
0
 def __init__(self, repo, limiter):
     self.repo = repo
     self.limiter = limiter
     for scope, attrs in ((base.version_scope, ['fullver', 'version', 'rev']),
                          (base.package_scope, ['package']),
                          (base.category_scope, ['category'])):
         if any(util.collect_package_restrictions(limiter, attrs)):
             self.scope = scope
             return
     self.scope = base.repository_scope
Ejemplo n.º 11
0
 def __init__(self, repo, limiter):
     self.repo = repo
     self.limiter = limiter
     for scope, attrs in ((base.version_scope, ['fullver', 'version', 'rev']),
                          (base.package_scope, ['package']),
                          (base.category_scope, ['category'])):
         if any(util.collect_package_restrictions(limiter, attrs)):
             self.scope = scope
             return
     self.scope = base.repository_scope
Ejemplo n.º 12
0
 def feed(self, pkg):
     for vuln in self.vulns.get(pkg.key, []):
         if vuln.match(pkg):
             arches = set()
             for v in collect_package_restrictions(vuln, ['keywords']):
                 if isinstance(v.restriction, values.ContainmentMatch2):
                     arches.update(x.lstrip('~') for x in v.restriction.vals)
                 else:
                     raise Exception(
                         f'unexpected restriction sequence- {v.restriction} in {vuln}')
             keys = {x.lstrip('~') for x in pkg.keywords if not x.startswith('-')}
             if arches:
                 arches = sorted(arches.intersection(keys))
                 assert arches
             else:
                 arches = sorted(keys)
             yield VulnerablePackage(arches, str(vuln), pkg=pkg)
Ejemplo n.º 13
0
def parse_match(text):
    """generate appropriate restriction for text

    Parsing basically breaks it down into chunks split by /, with each
    chunk allowing for prefix/postfix globbing- note that a postfixed
    glob on package token is treated as package attribute matching,
    not as necessarily a version match.

    If only one chunk is found, it's treated as a package chunk.
    Finally, it supports a nonstandard variation of atom syntax where
    the category can be dropped.

    Examples:

    - `*`: match all
    - `dev-*/*`: category must start with 'dev-'
    - `dev-*`: package must start with 'dev-'
    - `*-apps/portage*`: category must end in '-apps', package must start with
      'portage'
    - `>=portage-2.1`: atom syntax, package 'portage', version greater then or
      equal to '2.1'
    - dev-qt/*:5: all Qt 5 libs
    - boost:0/1.60: all packages named boost with a slot/subslot of 0/1.60.0

    :param text: string to attempt to parse
    :type text: string
    :return: :obj:`pkgcore.restrictions.packages` derivative
    """

    # Ensure the text var is a string if we're under py3k.
    if not is_py3k:
        text = text.encode('ascii')
    orig_text = text = text.strip()
    if "!" in text:
        raise ParseError(
            "'!' or any form of blockers make no sense in this usage: '%s'" %
            (text, ))

    restrictions = []
    if '::' in text:
        text, repo_id = text.rsplit('::', 1)
        restrictions.append(restricts.RepositoryDep(repo_id))
    if ':' in text:
        text, slot = text.rsplit(':', 1)
        slot, _sep, subslot = slot.partition('/')
        if slot:
            restrictions.append(restricts.SlotDep(slot))
        if subslot:
            restrictions.append(restricts.SubSlotDep(subslot))

    tsplit = text.rsplit("/", 1)
    if len(tsplit) == 1:
        ops, text = collect_ops(text)
        if not ops:
            if "*" in text:
                r = convert_glob(text)
                if r is None:
                    restrictions.append(packages.AlwaysTrue)
                else:
                    restrictions.append(
                        packages.PackageRestriction("package", r))
                if len(restrictions) == 1:
                    return restrictions[0]
                return packages.AndRestriction(*restrictions)
        elif text.startswith("*"):
            raise ParseError(
                "cannot do prefix glob matches with version ops: %s" %
                (orig_text, ))
        # ok... fake category.  whee.
        try:
            r = list(
                collect_package_restrictions(atom.atom(
                    "%scategory/%s" % (ops, text)).restrictions,
                                             attrs=("category", ),
                                             invert=True))
        except errors.MalformedAtom as e:
            e.atom = orig_text
            raise_from(ParseError(str(e)))
        if not restrictions and len(r) == 1:
            return r[0]
        restrictions.extend(r)
        return packages.AndRestriction(*restrictions)
    elif text[0] in "=<>~" or "*" not in text:
        try:
            return atom.atom(orig_text)
        except errors.MalformedAtom as e:
            raise_from(ParseError(str(e)))

    r = map(convert_glob, tsplit)
    if not r[0] and not r[1]:
        restrictions.append(packages.AlwaysTrue)
    elif not r[0]:
        restrictions.append(packages.PackageRestriction("package", r[1]))
    elif not r[1]:
        restrictions.append(packages.PackageRestriction("category", r[0]))
    else:
        restrictions.extend((
            packages.PackageRestriction("category", r[0]),
            packages.PackageRestriction("package", r[1]),
        ))
    if len(restrictions) == 1:
        return restrictions[0]
    return packages.AndRestriction(*restrictions)
Ejemplo n.º 14
0
def parse_match(text):
    """generate appropriate restriction for text

    Parsing basically breaks it down into chunks split by /, with each
    chunk allowing for prefix/postfix globbing- note that a postfixed
    glob on package token is treated as package attribute matching,
    not as necessarily a version match.

    If only one chunk is found, it's treated as a package chunk.
    Finally, it supports a nonstandard variation of atom syntax where
    the category can be dropped.

    Examples:

    - `*`: match all
    - `dev-*/*`: category must start with 'dev-'
    - `dev-*`: package must start with 'dev-'
    - `*-apps/portage*`: category must end in '-apps', package must start with
      'portage'
    - `>=portage-2.1`: atom syntax, package 'portage', version greater then or
      equal to '2.1'
    - dev-qt/*:5: all Qt 5 libs
    - boost:0/1.60: all packages named boost with a slot/subslot of 0/1.60.0

    :param text: string to attempt to parse
    :type text: string
    :return: :obj:`pkgcore.restrictions.packages` derivative
    """

    orig_text = text = text.strip()
    if "!" in text:
        raise ParseError(
            f"'!' or any form of blockers make no sense in this usage: {text!r}")

    restrictions = []
    if '::' in text:
        text, repo_id = text.rsplit('::', 1)
        restrictions.append(restricts.RepositoryDep(repo_id))
    if ':' in text:
        text, slot = text.rsplit(':', 1)
        slot, _sep, subslot = slot.partition('/')
        if slot:
            if '*' in slot:
                r = convert_glob(slot)
                if r is not None:
                    restrictions.append(packages.PackageRestriction("slot", r))
            else:
                restrictions.append(restricts.SlotDep(slot))
        if subslot:
            if '*' in subslot:
                if r is not None:
                    restrictions.append(packages.PackageRestriction("subslot", r))
            else:
                restrictions.append(restricts.SubSlotDep(subslot))

    tsplit = text.rsplit("/", 1)
    if len(tsplit) == 1:
        ops, text = collect_ops(text)
        if not ops:
            if "*" in text:
                r = convert_glob(text)
                if r is None:
                    restrictions.append(packages.AlwaysTrue)
                else:
                    restrictions.append(packages.PackageRestriction("package", r))
                if len(restrictions) == 1:
                    return restrictions[0]
                return packages.AndRestriction(*restrictions)
        elif text.startswith("*"):
            raise ParseError(
                f"cannot do prefix glob matches with version ops: {orig_text}")
        # ok... fake category.  whee.
        try:
            r = list(collect_package_restrictions(
                     atom.atom(f"{ops}category/{text}").restrictions,
                     attrs=("category",), invert=True))
        except errors.MalformedAtom as e:
            e.atom = orig_text
            raise ParseError(str(e)) from e
        if not restrictions and len(r) == 1:
            return r[0]
        restrictions.extend(r)
        return packages.AndRestriction(*restrictions)
    elif text[0] in atom.valid_ops or '*' not in text:
        # possibly a valid atom object
        try:
            return atom.atom(orig_text)
        except errors.MalformedAtom as e:
            if '*' not in text:
                raise ParseError(str(e)) from e
            # support globbed targets with version restrictions
            return packages.AndRestriction(*parse_globbed_version(text, orig_text))

    r = list(map(convert_glob, tsplit))
    if not r[0] and not r[1]:
        restrictions.append(packages.AlwaysTrue)
    elif not r[0]:
        restrictions.append(packages.PackageRestriction("package", r[1]))
    elif not r[1]:
        restrictions.append(packages.PackageRestriction("category", r[0]))
    else:
        restrictions.extend((
            packages.PackageRestriction("category", r[0]),
            packages.PackageRestriction("package", r[1]),
        ))
    if len(restrictions) == 1:
        return restrictions[0]
    return packages.AndRestriction(*restrictions)
Ejemplo n.º 15
0
    def _fast_identify_candidates(self, restrict, sorter):
        pkg_restrict = set()
        cat_restrict = set()
        cat_exact = set()
        pkg_exact = set()

        for x in collect_package_restrictions(restrict, (
                "category",
                "package",
        )):
            if x.attr == "category":
                cat_restrict.add(x.restriction)
            elif x.attr == "package":
                pkg_restrict.add(x.restriction)

        for e, s in ((pkg_exact, pkg_restrict), (cat_exact, cat_restrict)):
            l = [
                x for x in s
                if isinstance(x, values.StrExactMatch) and not x.negate
            ]
            s.difference_update(l)
            e.update(x.exact for x in l)
        del l

        if restrict.negate:
            cat_exact = pkg_exact = ()

        if cat_exact:
            if not cat_restrict and len(cat_exact) == 1:
                # Cannot use pop here, cat_exact is reused below.
                c = next(iter(cat_exact))
                if not pkg_restrict and len(pkg_exact) == 1:
                    cp = (c, pkg_exact.pop())
                    if cp in self.versions:
                        return [cp]
                    return []
                cats_iter = [c]
            else:
                cat_restrict.add(values.ContainmentMatch2(
                    frozenset(cat_exact)))
                cats_iter = sorter(self._cat_filter(cat_restrict))
        elif cat_restrict:
            cats_iter = self._cat_filter(cat_restrict, negate=restrict.negate)
        else:
            cats_iter = sorter(self.categories)

        if pkg_exact:
            if not pkg_restrict:
                if sorter is iter:
                    pkg_exact = tuple(pkg_exact)
                else:
                    pkg_exact = sorter(pkg_exact)
                return ((c, p) for c in cats_iter for p in pkg_exact)
            else:
                pkg_restrict.add(values.ContainmentMatch2(
                    frozenset(pkg_exact)))

        if pkg_restrict:
            return self._package_filter(cats_iter,
                                        pkg_restrict,
                                        negate=restrict.negate)
        elif not cat_restrict:
            if sorter is iter and not cat_exact:
                return self.versions
            else:
                return ((c, p) for c in cats_iter
                        for p in sorter(self.packages.get(c, ())))
        return ((c, p) for c in cats_iter
                for p in sorter(self.packages.get(c, ())))