Source code for pkgcheck.addons.eclass
"""Eclass specific support and addon."""
import os
from functools import total_ordering
from pkgcore.ebuild.eclass import EclassDoc
from snakeoil.klass import jit_attr_none
from snakeoil.mappings import ImmutableDict
from snakeoil.osutils import pjoin
from .. import base
from . import caches
[docs]
@total_ordering
class Eclass:
"""Generic eclass object."""
def __init__(self, name, path):
self.name = name
self.path = os.path.realpath(path)
def __str__(self):
return self.name
@property
def lines(self):
try:
with open(self.path) as f:
return tuple(f)
except FileNotFoundError:
return ()
def __lt__(self, other):
if isinstance(other, Eclass):
return self.name < other.name
return self.name < other
def __hash__(self):
return hash(self.path)
def __eq__(self, other):
if isinstance(other, Eclass):
return self.path == other.path
return self.path == other
[docs]
class EclassAddon(caches.CachedAddon):
"""Eclass support for various checks."""
# cache registry
cache = caches.CacheData(type="eclass", file="eclass.pickle", version=EclassDoc.ABI_VERSION)
def __init__(self, *args):
super().__init__(*args)
# mapping of repo locations to their corresponding eclass caches
self._eclass_repos = {}
@jit_attr_none
def eclasses(self, repo=None):
"""Mapping of available eclasses to eclass doc info."""
d = {}
for r in self.options.target_repo.trees:
d.update(self._eclass_repos.get(r.location, ()))
return ImmutableDict(d)
@jit_attr_none
def deprecated(self):
"""Mapping of deprecated eclasses to their replacements (if any)."""
d = {}
for r in self.options.target_repo.trees:
try:
for name, eclass in self._eclass_repos[r.location].items():
if eclass.deprecated:
d[name] = eclass.deprecated
except KeyError:
continue
return ImmutableDict(d)
[docs]
def update_cache(self, force=False):
"""Update related cache and push updates to disk."""
for repo in self.options.target_repo.trees:
eclass_dir = pjoin(repo.location, "eclass")
cache_file = self.cache_file(repo)
cache_eclasses = False
eclasses = {}
if not force:
eclasses = self.load_cache(cache_file, fallback={})
# check for eclass removals
for name in list(eclasses):
if not os.path.exists(pjoin(eclass_dir, f"{name}.eclass")):
del eclasses[name]
cache_eclasses = True
# verify the repo has eclasses
try:
repo_eclasses = sorted(
(x[:-7], pjoin(eclass_dir, x))
for x in os.listdir(eclass_dir)
if x.endswith(".eclass")
)
except FileNotFoundError:
repo_eclasses = []
if repo_eclasses:
# padding for progress output
padding = max(len(x[0]) for x in repo_eclasses)
# check for eclass additions and updates
with base.ProgressManager(verbosity=self.options.verbosity) as progress:
for name, path in repo_eclasses:
try:
if os.path.getmtime(path) != eclasses[name].mtime:
raise KeyError
except (KeyError, AttributeError):
try:
progress(f"{repo} -- updating eclass cache: {name:<{padding}}")
eclasses[name] = EclassDoc(path, sourced=True, repo=repo)
cache_eclasses = True
except IOError:
continue
if cache_eclasses:
# reset jit attrs
self._eclasses = None
self._deprecated = None
# push cache updates to disk
data = caches.DictCache(eclasses, self.cache)
self.save_cache(data, cache_file)
self._eclass_repos[repo.location] = eclasses