################################################## # SPYCE - Python-based HTML Scripting # Copyright (c) 2002 Rimon Barr. # # Refer to spyce.py # CVS: $Id: spyceCache.py 1095 2006-08-09 22:28:10Z ellisj $ ################################################## import spyceLock, spyceUtil import os, string from md5 import md5 try: from cPickle import dumps, loads except: from pickle import dumps, loads __doc__ = '''Caching related functionality.''' # rimtodo: specify some sort of cache size limit ################################################## # Generic cache # class cache: "Generic cache" def __getitem__(self, key): raise 'not implemented' def __setitem__(self, key, value): raise 'not implemented' def __delitem__(self, key): raise 'not implemented' def keys(self): raise 'not implemented' def has_key(self, key): raise 'not implemented' ################################################## # Storage caches # class memoryCache(cache): "In-memory cache" def __init__(self, infoStr=None): self.cache = {} self.info = infoStr def __getitem__(self, key): return self.cache[key] def __setitem__(self, key, value): self.cache[key]=value def __delitem__(self, key): del self.cache[key] def keys(self): return self.cache.keys() def has_key(self, key): return self.cache.has_key(key) class fileCache(cache): "File-based cache" def __init__(self, infoStr): self._cachedir = string.strip(infoStr) def _filename(self, key): return os.path.join(self._cachedir, self._encodeKey(key)) def __getitem__(self, key): f = None try: try: f = open(self._filename(key), 'rb') return loads(f.read()) finally: if f: f.close() except IOError: pass except EOFError: pass raise KeyError() def __setitem__(self, key, value): try: if self[key]==value: return except KeyError: pass f = None try: f = open(self._filename(key), 'wb') f.write(dumps(value, -1)) finally: if f: f.close() def __delitem__(self, key): filename = self._filename(key) if os.path.exists(filename): os.remove(filename) def keys(keys): raise 'not implemented' def has_key(self, key): try: self[key] return 1 except KeyError: return 0 def _encodeKey(self, key): return 'spyceCache-' + md5(str(key)).hexdigest() ################################################## # Policy caches # #rimtodo: ################################################## # Semantic cache # class semanticCache(cache): """Cache that knows how to validate and generate its own data. Note, that the cache stores elements as (validity, data) tuples. The valid is a function invoked as valid(key,validity), returning a boolean; and generate is a function invoked as generate(key) returning (validity, data). The get() method returns only the data.""" def __init__(self, cache, valid, generate, lock=None): self.valid = valid self.generate = generate self.cache = cache if lock is None: lock = spyceLock.dummyLock() self.lock = lock def get(self, key): "Get (or generate) a cache element." try: self.lock.acquire() except: # must be a file lock, rlock won't raise raise "couldn't lock %s -- did you set tmp to be a writable directory in your spyceconf?\n\nexact exception was %s" % (self.lock.name, spyceUtil.exceptionString()) try: if self.cache is None: return self.generate(key)[1] else: if not key in self.cache or not self.valid(key, self.cache[key][0]): self.cache[key] = self.generate(key) return self.cache[key][1] finally: self.lock.release() def purge(self, key): "Remove a cache element, if it exists." if self.cache.has_key(key): del self.cache[key] # standard dictionary methods def __getitem__(self, key): return self.get(key) def __delitem__(self, key): return self.purge(key) def has_key(self, key): if self.cache: return self.cache.has_key() else: return 0 def keys(self): if self.cache: return self.cache.keys() else: return [] def values(self): if self.cache: return map(lambda x: x[1], self.cache.values()) else: return [] def clear(self): if self.cache: self.cache.clear()