Source code for logilab.common.cache

# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-common.
#
# logilab-common is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 2.1 of the License, or (at your option) any
# later version.
#
# logilab-common 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 Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-common.  If not, see <http://www.gnu.org/licenses/>.
"""Cache module, with a least recently used algorithm for the management of the
deletion of entries.




"""
__docformat__ = "restructuredtext en"

from threading import Lock

from logilab.common.decorators import locked
from typing import TypeVar, List

_marker = object()


_KeyType = TypeVar("_KeyType")


[docs]class Cache(dict): """A dictionary like cache. inv: len(self._usage) <= self.size len(self.data) <= self.size """ def __init__(self, size: int = 100) -> None: """Warning : Cache.__init__() != dict.__init__(). Constructor does not take any arguments beside size. """ assert size >= 0, "cache size must be >= 0 (0 meaning no caching)" self.size = size self._usage: List = [] self._lock = Lock() super(Cache, self).__init__() def _acquire(self) -> None: self._lock.acquire() def _release(self) -> None: self._lock.release() def _update_usage(self, key: _KeyType) -> None: 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 dictionary # and remove the oldest item in the cache if self.size and len(self._usage) >= self.size: super(Cache, self).__delitem__(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: _KeyType): value = super(Cache, self).__getitem__(key) self._update_usage(key) return value __getitem__ = locked(_acquire, _release)(__getitem__) def __setitem__(self, key: _KeyType, item): # Just make sure that size > 0 before inserting a new item in the cache if self.size > 0: super(Cache, self).__setitem__(key, item) self._update_usage(key) __setitem__ = locked(_acquire, _release)(__setitem__) def __delitem__(self, key: _KeyType): super(Cache, self).__delitem__(key) self._usage.remove(key) __delitem__ = locked(_acquire, _release)(__delitem__) def clear(self): super(Cache, self).clear() self._usage = [] clear = locked(_acquire, _release)(clear) def pop(self, key: _KeyType, default=_marker): if key in self: self._usage.remove(key) # if default is _marker: # return super(Cache, self).pop(key) return super(Cache, self).pop(key, default) pop = locked(_acquire, _release)(pop)
[docs] def popitem(self): raise NotImplementedError()
[docs] def setdefault(self, key, default=None): raise NotImplementedError()
[docs] def update(self, other): raise NotImplementedError()