blob: 238f9dc40c40f7c7003b7251b12b05283aa4b577 [file] [log] [blame]
# Copyright: 2005 Gentoo Foundation
# Author(s): Brian Harring (ferringb@gentoo.org)
# License: GPL2
# $Id$
from portage.cache import cache_errors
from portage.cache.cache_errors import InvalidRestriction
from portage.cache.mappings import ProtectedDict
import warnings
class database(object):
# this is for metadata/cache transfer.
# basically flags the cache needs be updated when transfered cache to cache.
# leave this.
complete_eclass_entries = True
autocommits = False
cleanse_keys = False
serialize_eclasses = True
def __init__(self, location, label, auxdbkeys, readonly=False):
""" initialize the derived class; specifically, store label/keys"""
self._known_keys = auxdbkeys
self.location = location
self.label = label
self.readonly = readonly
self.sync_rate = 0
self.updates = 0
def __getitem__(self, cpv):
"""set a cpv to values
This shouldn't be overriden in derived classes since it handles the __eclasses__ conversion.
that said, if the class handles it, they can override it."""
if self.updates > self.sync_rate:
self.commit()
self.updates = 0
d=self._getitem(cpv)
if self.serialize_eclasses and "_eclasses_" in d:
d["_eclasses_"] = reconstruct_eclasses(cpv, d["_eclasses_"])
elif "_eclasses_" not in d:
d["_eclasses_"] = {}
return d
def _getitem(self, cpv):
"""get cpv's values.
override this in derived classess"""
raise NotImplementedError
def __setitem__(self, cpv, values):
"""set a cpv to values
This shouldn't be overriden in derived classes since it handles the readonly checks"""
if self.readonly:
raise cache_errors.ReadOnlyRestriction()
if self.cleanse_keys:
d=ProtectedDict(values)
for k in d.keys():
if d[k] == '':
del d[k]
if self.serialize_eclasses and "_eclasses_" in values:
d["_eclasses_"] = serialize_eclasses(d["_eclasses_"])
elif self.serialize_eclasses and "_eclasses_" in values:
d = ProtectedDict(values)
d["_eclasses_"] = serialize_eclasses(d["_eclasses_"])
else:
d = values
self._setitem(cpv, d)
if not self.autocommits:
self.updates += 1
if self.updates > self.sync_rate:
self.commit()
self.updates = 0
def _setitem(self, name, values):
"""__setitem__ calls this after readonly checks. override it in derived classes
note _eclassees_ key *must* be handled"""
raise NotImplementedError
def __delitem__(self, cpv):
"""delete a key from the cache.
This shouldn't be overriden in derived classes since it handles the readonly checks"""
if self.readonly:
raise cache_errors.ReadOnlyRestriction()
if not self.autocommits:
self.updates += 1
self._delitem(cpv)
if self.updates > self.sync_rate:
self.commit()
self.updates = 0
def _delitem(self,cpv):
"""__delitem__ calls this after readonly checks. override it in derived classes"""
raise NotImplementedError
def has_key(self, cpv):
return cpv in self
def keys(self):
return tuple(self.iterkeys())
def iterkeys(self):
return iter(self)
def iteritems(self):
for x in self.iterkeys():
yield (x, self[x])
def items(self):
return list(self.iteritems())
def sync(self, rate=0):
self.sync_rate = rate
if(rate == 0):
self.commit()
def commit(self):
if not self.autocommits:
raise NotImplementedError
def __contains__(self, cpv):
"""This method should always be overridden. It is provided only for
backward compatibility with modules that override has_key instead. It
will automatically raise a NotImplementedError if has_key has not been
overridden."""
if self.has_key is database.has_key:
# prevent a possible recursive loop
raise NotImplementedError
warnings.warn("portage.cache.template.database.has_key() is "
"deprecated, override __contains__ instead",
DeprecationWarning)
return self.has_key(cpv)
def __iter__(self):
"""This method should always be overridden. It is provided only for
backward compatibility with modules that override iterkeys instead. It
will automatically raise a NotImplementedError if iterkeys has not been
overridden."""
if self.iterkeys is database.iterkeys:
# prevent a possible recursive loop
raise NotImplementedError(self)
return self.iterkeys()
def get(self, k, x=None):
try:
return self[k]
except KeyError:
return x
def get_matches(self, match_dict):
"""generic function for walking the entire cache db, matching restrictions to
filter what cpv's are returned. Derived classes should override this if they
can implement a faster method then pulling each cpv:values, and checking it.
For example, RDBMS derived classes should push the matching logic down to the
actual RDBM."""
import re
restricts = {}
for key,match in match_dict.iteritems():
# XXX this sucks.
try:
if isinstance(match, str):
restricts[key] = re.compile(match).match
else:
restricts[key] = re.compile(match[0],match[1]).match
except re.error, e:
raise InvalidRestriction(key, match, e)
if key not in self.__known_keys:
raise InvalidRestriction(key, match, "Key isn't valid")
for cpv in self.iterkeys():
cont = True
vals = self[cpv]
for key, match in restricts.iteritems():
if not match(vals[key]):
cont = False
break
if cont:
yield cpv
def serialize_eclasses(eclass_dict):
"""takes a dict, returns a string representing said dict"""
"""The "new format", which causes older versions of <portage-2.1.2 to
traceback with a ValueError due to failed long() conversion. This format
isn't currently written, but the the capability to read it is already built
in.
return "\t".join(["%s\t%s" % (k, str(v)) \
for k, v in eclass_dict.iteritems()])
"""
if not eclass_dict:
return ""
return "\t".join(k + "\t%s\t%s" % eclass_dict[k] \
for k in sorted(eclass_dict))
def reconstruct_eclasses(cpv, eclass_string):
"""returns a dict when handed a string generated by serialize_eclasses"""
eclasses = eclass_string.rstrip().lstrip().split("\t")
if eclasses == [""]:
# occasionally this occurs in the fs backends. they suck.
return {}
if len(eclasses) % 2 != 0 and len(eclasses) % 3 != 0:
raise cache_errors.CacheCorruption(cpv, "_eclasses_ was of invalid len %i" % len(eclasses))
d={}
try:
if eclasses[1].isdigit():
for x in xrange(0, len(eclasses), 2):
d[eclasses[x]] = ("", long(eclasses[x + 1]))
else:
# The old format contains paths that will be discarded.
for x in xrange(0, len(eclasses), 3):
d[eclasses[x]] = (eclasses[x + 1], long(eclasses[x + 2]))
except ValueError:
raise cache_errors.CacheCorruption(cpv, "_eclasses_ mtime conversion to long failed")
del eclasses
return d