OLD | NEW |
(Empty) | |
| 1 # copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
| 2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr |
| 3 # |
| 4 # This file is part of logilab-common. |
| 5 # |
| 6 # logilab-common is free software: you can redistribute it and/or modify it unde
r |
| 7 # the terms of the GNU Lesser General Public License as published by the Free |
| 8 # Software Foundation, either version 2.1 of the License, or (at your option) an
y |
| 9 # later version. |
| 10 # |
| 11 # logilab-common is distributed in the hope that it will be useful, but WITHOUT |
| 12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| 13 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
| 14 # details. |
| 15 # |
| 16 # You should have received a copy of the GNU Lesser General Public License along |
| 17 # with logilab-common. If not, see <http://www.gnu.org/licenses/>. |
| 18 """Cache module, with a least recently used algorithm for the management of the |
| 19 deletion of entries. |
| 20 |
| 21 |
| 22 |
| 23 |
| 24 """ |
| 25 __docformat__ = "restructuredtext en" |
| 26 |
| 27 from threading import Lock |
| 28 |
| 29 from logilab.common.decorators import locked |
| 30 |
| 31 _marker = object() |
| 32 |
| 33 class Cache(dict): |
| 34 """A dictionary like cache. |
| 35 |
| 36 inv: |
| 37 len(self._usage) <= self.size |
| 38 len(self.data) <= self.size |
| 39 """ |
| 40 |
| 41 def __init__(self, size=100): |
| 42 """ Warning : Cache.__init__() != dict.__init__(). |
| 43 Constructor does not take any arguments beside size. |
| 44 """ |
| 45 assert size >= 0, 'cache size must be >= 0 (0 meaning no caching)' |
| 46 self.size = size |
| 47 self._usage = [] |
| 48 self._lock = Lock() |
| 49 super(Cache, self).__init__() |
| 50 |
| 51 def _acquire(self): |
| 52 self._lock.acquire() |
| 53 |
| 54 def _release(self): |
| 55 self._lock.release() |
| 56 |
| 57 def _update_usage(self, key): |
| 58 if not self._usage: |
| 59 self._usage.append(key) |
| 60 elif self._usage[-1] != key: |
| 61 try: |
| 62 self._usage.remove(key) |
| 63 except ValueError: |
| 64 # we are inserting a new key |
| 65 # check the size of the dictionary |
| 66 # and remove the oldest item in the cache |
| 67 if self.size and len(self._usage) >= self.size: |
| 68 super(Cache, self).__delitem__(self._usage[0]) |
| 69 del self._usage[0] |
| 70 self._usage.append(key) |
| 71 else: |
| 72 pass # key is already the most recently used key |
| 73 |
| 74 def __getitem__(self, key): |
| 75 value = super(Cache, self).__getitem__(key) |
| 76 self._update_usage(key) |
| 77 return value |
| 78 __getitem__ = locked(_acquire, _release)(__getitem__) |
| 79 |
| 80 def __setitem__(self, key, item): |
| 81 # Just make sure that size > 0 before inserting a new item in the cache |
| 82 if self.size > 0: |
| 83 super(Cache, self).__setitem__(key, item) |
| 84 self._update_usage(key) |
| 85 __setitem__ = locked(_acquire, _release)(__setitem__) |
| 86 |
| 87 def __delitem__(self, key): |
| 88 super(Cache, self).__delitem__(key) |
| 89 self._usage.remove(key) |
| 90 __delitem__ = locked(_acquire, _release)(__delitem__) |
| 91 |
| 92 def clear(self): |
| 93 super(Cache, self).clear() |
| 94 self._usage = [] |
| 95 clear = locked(_acquire, _release)(clear) |
| 96 |
| 97 def pop(self, key, default=_marker): |
| 98 if key in self: |
| 99 self._usage.remove(key) |
| 100 #if default is _marker: |
| 101 # return super(Cache, self).pop(key) |
| 102 return super(Cache, self).pop(key, default) |
| 103 pop = locked(_acquire, _release)(pop) |
| 104 |
| 105 def popitem(self): |
| 106 raise NotImplementedError() |
| 107 |
| 108 def setdefault(self, key, default=None): |
| 109 raise NotImplementedError() |
| 110 |
| 111 def update(self, other): |
| 112 raise NotImplementedError() |
| 113 |
| 114 |
OLD | NEW |