예제 #1
0
        class foo1:
            __slots__ = ('obj', )

            def __init__(self, obj):
                self.obj = obj

            __dir__ = klass.DirProxy('obj')
예제 #2
0
class Subproject:
    """Data on a subproject.

    :type inherit_members: C{bool}
    :ivar inherit_members: whether the parent project inherits members from this subproject
    """

    __slots__ = ('_ref', 'inherit_members', '_projects_xml', '_project')

    def __init__(self, ref, projects_xml, inherit_members=None):
        if ref is None:
            raise ValueError('ref for subproject must not be null')
        self._ref = ref
        self.inherit_members = inherit_members
        self._projects_xml = projects_xml

    @klass.jit_attr
    def project(self):
        try:
            return self._projects_xml.projects[self._ref]
        except KeyError:
            logger.error(
                f'projects.xml: subproject {self._ref!r} does not exist')
            return None

    __getattr__ = klass.GetAttrProxy('project')
    __dir__ = klass.DirProxy('project')
예제 #3
0
파일: base.py 프로젝트: ulm/pkgcore
class wrapper(base):

    __slots__ = ("_raw_pkg", "_domain")

    klass.inject_richcmp_methods_from_cmp(locals())

    def operations(self, domain, **kwds):
        return self._raw_pkg._operations(domain, self, **kwds)

    def __init__(self, raw_pkg):
        object.__setattr__(self, "_raw_pkg", raw_pkg)

    def __cmp__(self, other):
        if isinstance(other, wrapper):
            return cmp(self._raw_pkg, other._raw_pkg)
        return cmp(self._raw_pkg, other)

    def __eq__(self, other):
        if isinstance(other, wrapper):
            return cmp(self._raw_pkg, other._raw_pkg) == 0
        return cmp(self._raw_pkg, other) == 0

    def __ne__(self, other):
        return not self == other

    __getattr__ = klass.GetAttrProxy("_raw_pkg")
    __dir__ = klass.DirProxy("_raw_pkg")

    built = klass.alias_attr("_raw_pkg.built")
    versioned_atom = klass.alias_attr("_raw_pkg.versioned_atom")
    unversioned_atom = klass.alias_attr("_raw_pkg.unversioned_atom")
    is_supported = klass.alias_attr('_raw_pkg.is_supported')

    def __hash__(self):
        return hash(self._raw_pkg)
예제 #4
0
파일: central.py 프로젝트: ulm/pkgcore
class CompatConfigManager(object):
    def __init__(self, manager):
        self._manager = manager

    def __getattr__(self, attr):
        obj = getattr(self._manager, attr, _singleton)
        if obj is _singleton:
            obj = getattr(self._manager.objects, attr)
        return obj

    __dir__ = klass.DirProxy("_manager")
예제 #5
0
파일: central.py 프로젝트: shen390s/pkgcore
class CompatConfigManager:
    def __init__(self, manager):
        self._manager = manager

    def __getattr__(self, attr):
        if attr == '_manager':
            return object.__getattribute__(self, '_manager')
        obj = getattr(self._manager, attr, _singleton)
        if obj is _singleton:
            obj = getattr(self._manager.objects, attr)
        return obj

    __dir__ = klass.DirProxy("_manager")
예제 #6
0
class wrapper(base):

    __slots__ = ("_raw_pkg", "_domain")

    def operations(self, domain, **kwds):
        return self._raw_pkg._operations(domain, self, **kwds)

    def __init__(self, raw_pkg):
        object.__setattr__(self, "_raw_pkg", raw_pkg)

    def __eq__(self, other):
        if isinstance(other, wrapper):
            return self._raw_pkg == other._raw_pkg
        try:
            return self._raw_pkg == other
        except TypeError:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def __lt__(self, other):
        if isinstance(other, wrapper):
            return self._raw_pkg < other._raw_pkg
        return self._raw_pkg < other

    def __le__(self, other):
        return self.__lt__(other) or self.__eq__(other)

    def __gt__(self, other):
        if isinstance(other, wrapper):
            return self._raw_pkg > other._raw_pkg
        return self._raw_pkg > other

    def __ge__(self, other):
        return self.__gt__(other) or self.__eq__(other)

    __getattr__ = klass.GetAttrProxy("_raw_pkg")
    __dir__ = klass.DirProxy("_raw_pkg")

    _get_attr = klass.alias_attr("_raw_pkg._get_attr")
    built = klass.alias_attr("_raw_pkg.built")
    versioned_atom = klass.alias_attr("_raw_pkg.versioned_atom")
    unversioned_atom = klass.alias_attr("_raw_pkg.unversioned_atom")
    is_supported = klass.alias_attr('_raw_pkg.is_supported')

    def __hash__(self):
        return hash(self._raw_pkg)
예제 #7
0
파일: packages.py 프로젝트: sbraz/pkgcheck
class WrappedPkg:
    """Generic package wrapper used to inject attributes into package objects."""

    __slots__ = ('_pkg', )

    def __init__(self, pkg):
        self._pkg = pkg

    def __str__(self):
        return str(self._pkg)

    def __repr__(self):
        return repr(self._pkg)

    def __lt__(self, other):
        return self.versioned_atom < other.versioned_atom

    __getattr__ = klass.GetAttrProxy('_pkg')
    __dir__ = klass.DirProxy('_pkg')
예제 #8
0
        class foo1:
            def __init__(self, obj):
                self.obj = obj

            __dir__ = klass.DirProxy('obj')
예제 #9
0
class EbuildRepo:
    """Class for creating/manipulating ebuild repos."""

    def __init__(self, path, repo_id='fake', eapi='5', masters=(), arches=()):
        self.path = path
        self.arches = _FileSet(pjoin(self.path, 'profiles', 'arch.list'))
        self._today = datetime.today()
        try:
            os.makedirs(pjoin(path, 'profiles'))
            with open(pjoin(path, 'profiles', 'repo_name'), 'w') as f:
                f.write(f'{repo_id}\n')
            with open(pjoin(path, 'profiles', 'eapi'), 'w') as f:
                f.write(f'{eapi}\n')
            os.makedirs(pjoin(path, 'metadata'))
            with open(pjoin(path, 'metadata', 'layout.conf'), 'w') as f:
                f.write(textwrap.dedent(f"""\
                    masters = {' '.join(masters)}
                    cache-formats =
                    thin-manifests = true
                """))
            if arches:
                self.arches.update(arches)
            os.makedirs(pjoin(path, 'eclass'))
        except FileExistsError:
            pass
        self.sync()

    def sync(self):
        """Forcibly create underlying repo object avoiding cache usage."""
        # avoid issues loading modules that set signal handlers
        from pkgcore.ebuild import repo_objs, repository
        repo_config = repo_objs.RepoConfig(location=self.path, disable_inst_caching=True)
        self._repo = repository.UnconfiguredTree(self.path, repo_config=repo_config)

    def create_profiles(self, profiles):
        for p in profiles:
            os.makedirs(pjoin(self.path, 'profiles', p.path), exist_ok=True)
            with open(pjoin(self.path, 'profiles', 'profiles.desc'), 'a+') as f:
                f.write(f'{p.arch} {p.path} {p.status}\n')
            if p.deprecated:
                with open(pjoin(self.path, 'profiles', p.path, 'deprecated'), 'w') as f:
                    f.write("# deprecated\ndeprecation reason\n")
            with open(pjoin(self.path, 'profiles', p.path, 'make.defaults'), 'w') as f:
                if p.defaults is not None:
                    f.write('\n'.join(p.defaults))
                else:
                    f.write(f'ARCH={p.arch}\n')
            if p.eapi:
                with open(pjoin(self.path, 'profiles', p.path, 'eapi'), 'w') as f:
                    f.write(f'{p.eapi}\n')

    def create_ebuild(self, cpvstr, data=None, **kwargs):
        from pkgcore.ebuild import cpv as cpv_mod
        cpv = cpv_mod.VersionedCPV(cpvstr)
        self._repo.notify_add_package(cpv)
        ebuild_dir = pjoin(self.path, cpv.category, cpv.package)
        os.makedirs(ebuild_dir, exist_ok=True)

        # use defaults for some ebuild metadata if unset
        eapi = kwargs.pop('eapi', '7')
        slot = kwargs.pop('slot', '0')
        desc = kwargs.pop('description', 'stub package description')
        homepage = kwargs.pop('homepage', 'https://github.com/pkgcore/pkgcheck')
        license = kwargs.pop('license', 'blank')

        ebuild_path = pjoin(ebuild_dir, f'{cpv.package}-{cpv.fullver}.ebuild')
        with open(ebuild_path, 'w') as f:
            if self.repo_id == 'gentoo':
                f.write(textwrap.dedent(f"""\
                    # Copyright 1999-{self._today.year} Gentoo Authors
                    # Distributed under the terms of the GNU General Public License v2
                """))
            f.write(f'EAPI="{eapi}"\n')
            f.write(f'DESCRIPTION="{desc}"\n')
            f.write(f'HOMEPAGE="{homepage}"\n')
            f.write(f'SLOT="{slot}"\n')

            if license:
                f.write(f'LICENSE="{license}"\n')
                # create a fake license
                os.makedirs(pjoin(self.path, 'licenses'), exist_ok=True)
                touch(pjoin(self.path, 'licenses', license))

            for k, v in kwargs.items():
                # handle sequences such as KEYWORDS and IUSE
                if isinstance(v, (tuple, list)):
                    v = ' '.join(v)
                f.write(f'{k.upper()}="{v}"\n')
            if data:
                f.write(data.strip() + '\n')

        return ebuild_path

    def __iter__(self):
        yield from iter(self._repo)

    __getattr__ = klass.GetAttrProxy('_repo')
    __dir__ = klass.DirProxy('_repo')
예제 #10
0
class atom(boolean.AndRestriction, metaclass=klass.generic_equality):
    """Currently implements gentoo ebuild atom parsing.

    Should be converted into an agnostic dependency base.
    """

    # note we don't need _hash
    __slots__ = (
        "blocks",
        "blocks_strongly",
        "op",
        "cpvstr",
        "negate_vers",
        "use",
        "slot_operator",
        "slot",
        "subslot",
        "repo_id",
        "_hash",
        "_cpv",
        "_restrictions",
    )

    type = restriction.package_type

    negate = False

    _evaluate_collapse = True

    __attr_comparison__ = ("cpvstr", "op", "blocks", "negate_vers", "use",
                           "slot", "subslot", "slot_operator", "repo_id")

    klass.inject_richcmp_methods_from_cmp(locals())
    # hack; combine these 2 metaclasses at some point...
    locals().pop("__eq__", None)
    locals().pop("__ne__", None)
    __inst_caching__ = True

    # overrided in child class if it's supported
    evaluate_depset = None

    def __init__(self, atom, negate_vers=False, eapi='-1'):
        """
        :param atom: string, see gentoo ebuild atom syntax
        :keyword negate_vers: boolean controlling whether the version should be
            inverted for restriction matching
        :keyword eapi: string/int controlling what eapi to enforce for this atom
        """
        if not atom:
            raise errors.MalformedAtom(atom)

        sf = object.__setattr__
        orig_atom = atom
        override_kls = False
        use_start = atom.find("[")
        slot_start = atom.find(":")

        if use_start != -1:
            # use dep
            use_end = atom.find("]", use_start)
            if use_end == -1:
                raise errors.MalformedAtom(orig_atom,
                                           "use restriction isn't completed")
            elif use_end != len(atom) - 1:
                raise errors.MalformedAtom(orig_atom,
                                           "trailing garbage after use dep")
            sf(self, "use",
               tuple(sorted(atom[use_start + 1:use_end].split(','))))
            for x in self.use:
                # stripped purely for validation reasons
                try:
                    if x[-1] in "=?":
                        override_kls = True
                        x = x[:-1]
                        if x[0] == '!':
                            x = x[1:]
                        if x[0] == '-':
                            raise errors.MalformedAtom(
                                orig_atom, f"malformed use flag: {x}")
                    elif x[0] == '-':
                        x = x[1:]

                    if x[-1] == ')' and eapi not in ('0', '1', '2', '3'):
                        # use defaults.
                        if x[-3:] in ("(+)", "(-)"):
                            x = x[:-3]

                    if not x:
                        raise errors.MalformedAtom(orig_atom,
                                                   'empty use dep detected')
                    if not valid_use_flag.match(x):
                        raise errors.MalformedAtom(orig_atom,
                                                   f'invalid USE flag: {x!r}')
                except IndexError:
                    raise errors.MalformedAtom(orig_atom,
                                               'empty use dep detected')
            if override_kls:
                sf(self, '__class__', transitive_use_atom)
            atom = atom[0:use_start] + atom[use_end + 1:]
        else:
            sf(self, "use", None)
        if slot_start != -1:
            i2 = atom.find("::", slot_start)
            if i2 != -1:
                repo_id = atom[i2 + 2:]
                if not repo_id:
                    raise errors.MalformedAtom(orig_atom,
                                               "repo_id must not be empty")
                elif repo_id[0] in '-':
                    raise errors.MalformedAtom(
                        orig_atom,
                        f"invalid first char of repo_id '{repo_id}' (must not begin with a hyphen)"
                    )
                elif not valid_repo_chars.issuperset(repo_id):
                    raise errors.MalformedAtom(
                        orig_atom,
                        f"repo_id may contain only [a-Z0-9_-/], found {repo_id!r}"
                    )
                atom = atom[:i2]
                sf(self, "repo_id", repo_id)
            else:
                sf(self, "repo_id", None)
            # slot dep.
            slot = atom[slot_start + 1:]
            slot_operator = subslot = None
            if not slot:
                # if the slot char came in only due to repo_id, force slots to None
                if i2 == -1:
                    raise errors.MalformedAtom(
                        orig_atom, "Empty slot targets aren't allowed")
                slot = None
            else:
                slots = (slot, )
                if eapi not in ('0', '1', '2', '3', '4'):
                    if slot[0:1] in ("*", "="):
                        if len(slot) > 1:
                            raise errors.MalformedAtom(
                                orig_atom,
                                "Slot operators '*' and '=' do not take slot targets"
                            )
                        slot_operator = slot
                        slot, slots = None, ()
                    else:
                        if slot.endswith('='):
                            slot_operator = '='
                            slot = slot[:-1]
                        slots = slot.split('/', 1)
                elif eapi == '0':
                    raise errors.MalformedAtom(
                        orig_atom,
                        "slot dependencies aren't allowed in EAPI 0")

                for chunk in slots:
                    if not chunk:
                        raise errors.MalformedAtom(
                            orig_atom, "Empty slot targets aren't allowed")

                    if chunk[0] in '-.':
                        raise errors.MalformedAtom(
                            orig_atom,
                            "Slot targets must not start with a hypen or dot: {chunk!r}"
                        )
                    elif not valid_slot_chars.issuperset(chunk):
                        invalid_chars = ', '.join(
                            map(
                                repr,
                                sorted(
                                    set(chunk).difference(valid_slot_chars))))
                        raise errors.MalformedAtom(
                            orig_atom,
                            f"Invalid character(s) in slot target: {invalid_chars}"
                        )

                if len(slots) == 2:
                    slot, subslot = slots

            sf(self, "slot_operator", slot_operator)
            sf(self, "slot", slot)
            sf(self, "subslot", subslot)
            atom = atom[:slot_start]
        else:
            sf(self, "slot_operator", None)
            sf(self, "slot", None)
            sf(self, "subslot", None)
            sf(self, "repo_id", None)

        sf(self, "blocks", atom[0] == "!")
        if self.blocks:
            atom = atom[1:]
            # hackish/slow, but lstrip doesn't take a 'prune this many' arg
            # open to alternatives
            if eapi not in ('0', '1') and atom.startswith("!"):
                atom = atom[1:]
                sf(self, "blocks_strongly", True)
            else:
                sf(self, "blocks_strongly", False)
        else:
            sf(self, "blocks_strongly", False)

        if atom[0] in ('<', '>'):
            if atom[1] == '=':
                sf(self, 'op', atom[:2])
                atom = atom[2:]
            else:
                sf(self, 'op', atom[0])
                atom = atom[1:]
        elif atom[0] == '=':
            if atom[-1] == '*':
                sf(self, 'op', '=*')
                atom = atom[1:-1]
            else:
                atom = atom[1:]
                sf(self, 'op', '=')
        elif atom[0] == '~':
            sf(self, 'op', '~')
            atom = atom[1:]
        else:
            sf(self, 'op', '')
        sf(self, 'cpvstr', atom)

        if eapi == '0':
            for x in ('use', 'slot'):
                if getattr(self, x) is not None:
                    raise errors.MalformedAtom(
                        orig_atom, f"{x} atoms aren't supported for EAPI 0")
        elif eapi == '1':
            if self.use is not None:
                raise errors.MalformedAtom(
                    orig_atom, "use atoms aren't supported for EAPI < 2")
        if eapi != '-1':
            if self.repo_id is not None:
                raise errors.MalformedAtom(
                    orig_atom,
                    f"repo_id atoms aren't supported for EAPI {eapi}")
        if use_start != -1 and slot_start != -1 and use_start < slot_start:
            raise errors.MalformedAtom(orig_atom,
                                       "slot restriction must proceed use")
        try:
            sf(self, "_cpv", cpv.CPV(self.cpvstr, versioned=bool(self.op)))
        except errors.InvalidCPV as e:
            raise errors.MalformedAtom(orig_atom) from e

        if self.op:
            if self.version is None:
                raise errors.MalformedAtom(orig_atom,
                                           "operator requires a version")
            elif self.op == '~' and self.revision:
                raise errors.MalformedAtom(
                    orig_atom,
                    "~ revision operater cannot be combined with a revision")
        elif self.version is not None:
            raise errors.MalformedAtom(orig_atom,
                                       'versioned atom requires an operator')
        sf(self, "_hash", hash(orig_atom))
        sf(self, "negate_vers", negate_vers)

    __getattr__ = klass.GetAttrProxy("_cpv")
    __dir__ = klass.DirProxy("_cpv")

    @property
    def blocks_temp_ignorable(self):
        return not self.blocks_strongly

    weak_blocker = klass.alias_attr("blocks_temp_ignorable")

    def __repr__(self):
        if self.op == '=*':
            atom = f"={self.cpvstr}*"
        else:
            atom = self.op + self.cpvstr
        if self.blocks:
            atom = '!' + atom
        if self.blocks:
            if self.blocks_strongly:
                atom = '!!' + atom
            else:
                atom = '!' + atom
        attrs = [atom]
        if self.use:
            attrs.append(f'use={self.use!r}')
        if self.slot is not None:
            attrs.append(f'slot={self.slot!r}')
        if self.subslot is not None:
            attrs.append(f'subslot={self.subslot!r}')
        if self.repo_id is not None:
            attrs.append(f'repo_id={self.repo_id!r}')
        return '<%s %s @#%x>' % (self.__class__.__name__, ' '.join(attrs),
                                 id(self))

    def __reduce__(self):
        return (atom, (str(self), self.negate_vers))

    def iter_dnf_solutions(self, full_solution_expansion=False):
        if full_solution_expansion:
            return boolean.AndRestriction.iter_dnf_solutions(self, True)
        return iter([[self]])

    def iter_cnf_solutions(self, full_solution_expansion=False):
        if full_solution_expansion:
            return boolean.AndRestriction.iter_cnf_solutions(self, True)
        return iter([[self]])

    def cnf_solutions(self, full_solution_expansion=False):
        if full_solution_expansion:
            return boolean.AndRestriction.cnf_solutions(self, True)
        return [[self]]

    @property
    def is_simple(self):
        return len(self.restrictions) == 2

    @klass.jit_attr
    def restrictions(self):
        # ordering here matters; against 24702 ebuilds for
        # a non matchable atom with package as the first restriction
        # 10 loops, best of 3: 206 msec per loop
        # with category as the first(the obvious ordering)
        # 10 loops, best of 3: 209 msec per loop
        # why?  because category is more likely to collide;
        # at the time of this profiling, there were 151 categories.
        # over 11k packages however.
        r = [
            restricts.PackageDep(self.package),
            restricts.CategoryDep(self.category)
        ]

        if self.repo_id is not None:
            r.insert(0, restricts.RepositoryDep(self.repo_id))

        if self.fullver is not None:
            if self.op == '=*':
                r.append(
                    packages.PackageRestriction(
                        "fullver", values.StrGlobMatch(self.fullver)))
            else:
                r.append(
                    restricts.VersionMatch(self.op,
                                           self.version,
                                           self.revision,
                                           negate=self.negate_vers))

        if self.slot is not None:
            r.append(restricts.SlotDep(self.slot))
            if self.subslot is not None:
                r.append(restricts.SubSlotDep(self.subslot))

        if self.use is not None:
            r.extend(restricts._parse_nontransitive_use(self.use))

        return tuple(r)

    def __str__(self):
        if self.op == '=*':
            s = f"={self.cpvstr}*"
        else:
            s = self.op + self.cpvstr
        if self.blocks:
            if self.blocks_strongly:
                s = '!!' + s
            else:
                s = '!' + s
        if self.slot:
            s += f":{self.slot}"
            if self.subslot:
                s += f"/{self.subslot}"
            if self.slot_operator == "=":
                s += self.slot_operator
        elif self.slot_operator:
            s += f":{self.slot_operator}"
        if self.repo_id:
            s += f"::{self.repo_id}"
        if self.use:
            use = ','.join(self.use)
            s += f"[{use}]"
        return s

    __hash__ = klass.reflective_hash('_hash')

    def __iter__(self):
        return iter(self.restrictions)

    def __getitem__(self, index):
        return self.restrictions[index]

    def __cmp__(self, other):
        if not isinstance(other, atom):
            raise TypeError(
                f"other isn't of {atom!r} type, is {other.__class__}")

        c = cmp(self.category, other.category)
        if c:
            return c

        c = cmp(self.package, other.package)
        if c:
            return c

        c = cmp(self.op, other.op)
        if c:
            return c

        c = cpv.ver_cmp(self.version, self.revision, other.version,
                        other.revision)
        if c:
            return c

        c = cmp(self.blocks, other.blocks)
        if c:
            # invert it; cmp(True, False) == 1
            # want non blockers then blockers.
            return -c

        c = cmp(self.blocks_strongly, other.blocks_strongly)
        if c:
            # want !! prior to !
            return c

        c = cmp(self.negate_vers, other.negate_vers)
        if c:
            return c

        def f(v):
            return '' if v is None else v

        c = cmp(f(self.slot), f(other.slot))
        if c:
            return c

        c = cmp(self.use, other.use)
        if c:
            return c

        return cmp(self.repo_id, other.repo_id)

    no_usedeps = klass.alias_attr("get_atom_without_use_deps")

    @property
    def get_atom_without_use_deps(self):
        """Return atom object stripped of USE dependencies."""
        if not self.use:
            return self
        if self.op == '=*':
            s = f'={self.cpvstr}*'
        else:
            s = self.op + self.cpvstr
        if self.blocks:
            s = '!' + s
            if not self.blocks_temp_ignorable:
                s = '!' + s
        if self.slot:
            s += f':{self.slot}'
        return atom(s)

    def intersects(self, other):
        """Check if a passed in atom "intersects" this restriction's atom.

        Two atoms "intersect" if a package can be constructed that
        matches both:

        - if you query for just "dev-lang/python" it "intersects" both
          "dev-lang/python" and ">=dev-lang/python-2.4"
        - if you query for "=dev-lang/python-2.4" it "intersects"
          ">=dev-lang/python-2.4" and "dev-lang/python" but not
          "<dev-lang/python-2.3"

        USE and slot deps are also taken into account.

        The block/nonblock state of the atom is ignored.
        """
        # Our "key" (cat/pkg) must match exactly:
        if self.key != other.key:
            return False

        # Slot dep only matters if we both have one. If we do they
        # must be identical:
        if (self.slot is not None and other.slot is not None
                and self.slot != other.slot):
            return False

        # Subslot dep only matters if we both have one. If we do they
        # must be identical:
        if (self.subslot is not None and other.subslot is not None
                and self.subslot != other.subslot):
            return False

        if (self.repo_id is not None and other.repo_id is not None
                and self.repo_id != other.repo_id):
            return False

        # Use deps are similar: if one of us forces a flag on and the
        # other forces it off we do not intersect. If only one of us
        # cares about a flag it is irrelevant.

        # Skip the (very common) case of one of us not having use deps:
        if self.use and other.use:
            # Set of flags we do not have in common:
            flags = set(self.use) ^ set(other.use)
            for flag in flags:
                # If this is unset and we also have the set version we fail:
                if flag[0] == '-' and flag[1:] in flags:
                    return False

        # Remaining thing to check is version restrictions. Get the
        # ones we can check without actual version comparisons out of
        # the way first.

        # If one of us is unversioned we intersect:
        if not self.op or not other.op:
            return True

        # If we are both "unbounded" in the same direction we intersect:
        if (('<' in self.op and '<' in other.op)
                or ('>' in self.op and '>' in other.op)):
            return True

        # Trick used here: just use the atoms as sufficiently
        # package-like object to pass to these functions (all that is
        # needed is a version and revision attr).

        # If one of us is an exact match we intersect if the other matches it:
        if self.op == '=':
            if other.op == '=*':
                return self.fullver.startswith(other.fullver)
            return restricts.VersionMatch(other.op, other.version,
                                          other.revision).match(self)
        if other.op == '=':
            if self.op == '=*':
                return other.fullver.startswith(self.fullver)
            return restricts.VersionMatch(self.op, self.version,
                                          self.revision).match(other)

        # If we are both ~ matches we match if we are identical:
        if self.op == other.op == '~':
            return (self.version == other.version
                    and self.revision == other.revision)

        # If we are both glob matches we match if one of us matches the other.
        if self.op == other.op == '=*':
            return (self.fullver.startswith(other.fullver)
                    or other.fullver.startswith(self.fullver))

        # If one of us is a glob match and the other a ~ we match if the glob
        # matches the ~ (ignoring a revision on the glob):
        if self.op == '=*' and other.op == '~':
            return other.fullver.startswith(self.version)
        if other.op == '=*' and self.op == '~':
            return self.fullver.startswith(other.version)

        # If we get here at least one of us is a <, <=, > or >=:
        if self.op in ('<', '<=', '>', '>='):
            ranged, other = self, other
        else:
            ranged, other = other, self

        if '<' in other.op or '>' in other.op:
            # We are both ranged, and in the opposite "direction" (or
            # we would have matched above). We intersect if we both
            # match the other's endpoint (just checking one endpoint
            # is not enough, it would give a false positive on <=2 vs >2)
            return (restricts.VersionMatch(other.op, other.version,
                                           other.revision).match(ranged)
                    and restricts.VersionMatch(ranged.op, ranged.version,
                                               ranged.revision).match(other))

        if other.op == '~':
            # Other definitely matches its own version. If ranged also
            # does we're done:
            if restricts.VersionMatch(ranged.op, ranged.version,
                                      ranged.revision).match(other):
                return True
            # The only other case where we intersect is if ranged is a
            # > or >= on other's version and a nonzero revision. In
            # that case other will match ranged. Be careful not to
            # give a false positive for ~2 vs <2 here:
            return ranged.op in ('>', '>=') and restricts.VersionMatch(
                other.op, other.version, other.revision).match(ranged)

        if other.op == '=*':
            # The fun one, since glob matches do not correspond to a
            # single contiguous region of versions.

            # a glob match definitely matches its own version, so if
            # ranged does too we're done:
            if restricts.VersionMatch(ranged.op, ranged.version,
                                      ranged.revision).match(other):
                return True
            if '<' in ranged.op:
                # Remaining cases where this intersects: there is a
                # package smaller than ranged.fullver and
                # other.fullver that they both match.

                # If other.revision is not None or 0 then other does not match
                # anything smaller than its own fullver:
                if other.revision:
                    return False

                # If other.revision is None or 0 then we can always
                # construct a package smaller than other.fullver by
                # tagging e.g. an _alpha1 on, since
                # cat/pkg_beta2_alpha1_alpha1 is a valid version.
                # (Yes, really. Try it if you don't believe me.)
                # If and only if other also matches ranged then
                # ranged will also match one of those smaller packages.
                # XXX (I think, need to try harder to verify this.)
                return ranged.fullver.startswith(other.version)
            else:
                # Remaining cases where this intersects: there is a
                # package greater than ranged.fullver and
                # other.fullver that they both match.

                # We can always construct a package greater than
                # other.fullver by adding a digit to it.
                # If and only if other also matches ranged then
                # ranged will match such a larger package
                # XXX (I think, need to try harder to verify this.)
                return ranged.fullver.startswith(other.version)

        # Handled all possible ops.
        raise NotImplementedError(
            'Someone added an op to atom without adding it to intersects')

    def evaluate_conditionals(self,
                              parent_cls,
                              parent_seq,
                              enabled,
                              tristate=None):
        parent_seq.append(self)