# This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; either version 2 of the License, or (at your option) any later # version. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. """ Copyright (c) 2002-2003 LOGILAB S.A. (Paris, FRANCE). http://www.logilab.fr/ -- mailto:contact@logilab.fr Cache module, with a least recently used algorithm for the management of the deletion of entries. """ from threading import Lock from logilab.common.decorators import locked _marker = object() class Cache: """ a dictionnary like cache inv: len(self._usage) <= self.size len(self.data) <= self.size """ def __init__(self, size=100): assert size >= 0, 'cache size must be >= 0 (0 meaning no caching)' self.data = {} self.size = size self._usage = [] self._lock = Lock() def __repr__(self): return repr(self.data) def __len__(self): return len(self.data) def _acquire(self): self._lock.acquire() def _release(self): self._lock.release() def _update_usage(self, key): if not self._usage: self._usage.append(key) elif self._usage[-1] != key: try: self._usage.remove(key) except ValueError: # we are inserting a new key # check the size of the dictionnary # and remove the oldest item in the cache if self.size and len(self._usage) >= self.size: del self.data[self._usage[0]] del self._usage[0] self._usage.append(key) else: pass # key is already the most recently used key def __getitem__(self, key): value = self.data[key] self._update_usage(key) return value __getitem__ = locked(_acquire, _release)(__getitem__) def __setitem__(self, key, item): # Just make sure that size > 0 before inserting a new item in the cache if self.size > 0: self.data[key] = item self._update_usage(key) __setitem__ = locked(_acquire, _release)(__setitem__) def __delitem__(self, key): del self.data[key] self._usage.remove(key) __delitem__ = locked(_acquire, _release)(__delitem__) def pop(self, value, default=_marker): if value in self.data: self._usage.remove(value) if default is _marker: return self.data.pop(value) return self.data.pop(value, default) pop = locked(_acquire, _release)(pop) def clear(self): self.data.clear() self._usage = [] clear = locked(_acquire, _release)(clear) def keys(self): return self.data.keys() def items(self): return self.data.items() def values(self): return self.data.values() def has_key(self, key): return self.data.has_key(key) __contains__ = has_key