| Index: third_party/logilab/common/cache.py | 
| diff --git a/third_party/logilab/common/cache.py b/third_party/logilab/common/cache.py | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..11ed1370d581852a5722747437574c2843e5deec | 
| --- /dev/null | 
| +++ b/third_party/logilab/common/cache.py | 
| @@ -0,0 +1,114 @@ | 
| +# 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 | 
| + | 
| +_marker = object() | 
| + | 
| +class Cache(dict): | 
| +    """A dictionary like cache. | 
| + | 
| +    inv: | 
| +        len(self._usage) <= self.size | 
| +        len(self.data) <= self.size | 
| +    """ | 
| + | 
| +    def __init__(self, size=100): | 
| +        """ 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 = [] | 
| +        self._lock = Lock() | 
| +        super(Cache, self).__init__() | 
| + | 
| +    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 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): | 
| +        value = super(Cache, self).__getitem__(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: | 
| +            super(Cache, self).__setitem__(key, item) | 
| +            self._update_usage(key) | 
| +    __setitem__ = locked(_acquire, _release)(__setitem__) | 
| + | 
| +    def __delitem__(self, key): | 
| +        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, 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) | 
| + | 
| +    def popitem(self): | 
| +        raise NotImplementedError() | 
| + | 
| +    def setdefault(self, key, default=None): | 
| +        raise NotImplementedError() | 
| + | 
| +    def update(self, other): | 
| +        raise NotImplementedError() | 
| + | 
| + | 
|  |