Source code for pkgcore.ebuild.eclass_cache

"""
in memory representation of on disk eclass stacking order
"""

__all__ = ("base", "cache", "StackedCaches")

import os
from sys import intern
from weakref import WeakValueDictionary

from snakeoil.chksum import LazilyHashedPath
from snakeoil.data_source import local_source
from snakeoil.klass import jit_attr_ext_method
from snakeoil.mappings import ImmutableDict, OrderedFrozenSet, StackedDict
from snakeoil.osutils import listdir_files, normpath, pjoin

from ..config.hint import ConfigHint


[docs] class base: """ Maintains the cache information about eclasses available to an ebuild. """ def __init__(self, location=None, eclassdir=None): self._eclass_data_inst_cache = WeakValueDictionary() # generate this. # self.eclasses = {} # {"Name": ("location", "_mtime_")} self.location = location self.eclassdir = eclassdir
[docs] def get_eclass_data(self, inherits): """Return the cachable entries from a list of inherited eclasses. Only make get_eclass_data calls for data you know came from this eclass_cache, otherwise be ready to catch a KeyError exception for any eclass that was requested, but not known to this cache. """ keys = OrderedFrozenSet(inherits) o = self._eclass_data_inst_cache.get(keys) if o is None: o = ImmutableDict((k, self.eclasses[k]) for k in keys) self._eclass_data_inst_cache[keys] = o return o
[docs] def get_eclass(self, eclass): o = self.eclasses.get(eclass) if o is None: return None return local_source(o.path)
eclasses = jit_attr_ext_method("_load_eclasses", "_eclasses")
[docs] def rebuild_cache_entry(self, entry_eclasses): """Check if eclass data is still valid. Given a dict as returned by get_eclass_data, walk it comparing it to internal eclass view. :return: a boolean representing whether that eclass data is still up to date, or not """ ec = self.eclasses d = {} for eclass, chksums in entry_eclasses: data = ec.get(eclass) if any(val != getattr(data, chf, None) for chf, val in chksums): return None d[eclass] = data return d
def __getstate__(self): d = self.__dict__.copy() del d["_eclass_data_inst_cache"] return d def __setstate__(self, state): self.__dict__ = state.copy() self.__dict__["_eclass_data_inst_cache"] = WeakValueDictionary()
[docs] class cache(base): pkgcore_config_type = ConfigHint( types={"path": "str", "location": "str"}, typename="eclass_cache" ) def __init__(self, path, location=None): """ :param location: ondisk location of the tree we're working with """ base.__init__(self, location=location, eclassdir=normpath(path)) def _load_eclasses(self): """Force an update of the internal view of on disk/remote eclasses.""" ec = {} eclass_len = len(".eclass") try: files = listdir_files(self.eclassdir) except (FileNotFoundError, NotADirectoryError): return ImmutableDict() for y in sorted(files): if not y.endswith(".eclass"): continue ys = y[:-eclass_len] ec[intern(ys)] = LazilyHashedPath( pjoin(self.eclassdir, y), eclassdir=self.eclassdir ) return ImmutableDict(ec)
[docs] class StackedCaches(base): """ collapse multiple eclass caches into one. Does L->R searching for eclass matches. """ pkgcore_config_type = ConfigHint( types={"caches": "refs:eclass_cache", "location": "str", "eclassdir": "str"}, typename="eclass_cache", ) def __init__(self, caches, **kwds): """ :param caches: :obj:`cache` instances to stack; ordering should be desired lookup order :keyword eclassdir: override for the master eclass dir, required for eapi0 and idiot eclass usage. defaults to pulling from the first cache. """ if len(caches) < 2: raise TypeError("%s requires at least two eclass_caches" % self.__class__) kwds.setdefault("eclassdir", caches[0].eclassdir) kwds.setdefault( "location", os.path.dirname(kwds["eclassdir"].rstrip(os.path.sep)) ) self._caches = caches base.__init__(self, **kwds) def _load_eclasses(self): return StackedDict(*[ec.eclasses for ec in self._caches])