diff --git a/attic/lrucache.py b/attic/lrucache.py index 3bb49fbc4..d4ea8a490 100644 --- a/attic/lrucache.py +++ b/attic/lrucache.py @@ -1,15 +1,16 @@ class LRUCache(dict): - def __init__(self, capacity): + def __init__(self, capacity, dispose): super(LRUCache, self).__init__() self._lru = [] self._capacity = capacity + self._dispose = dispose def __setitem__(self, key, value): - try: - self._lru.remove(key) - except ValueError: - pass + assert key not in self, ( + "Unexpected attempt to replace a cached item." + " If this is intended, please delete or pop the old item first." + " The dispose function will be called on delete (but not pop).") self._lru.append(key) while len(self._lru) > self._capacity: del self[self._lru[0]] @@ -28,7 +29,11 @@ class LRUCache(dict): self._lru.remove(key) except ValueError: pass - return super(LRUCache, self).__delitem__(key) + error = KeyError(key) + removed = super(LRUCache, self).pop(key, error) + if removed == error: + raise error + self._dispose(removed) def pop(self, key, default=None): try: @@ -37,6 +42,11 @@ class LRUCache(dict): pass return super(LRUCache, self).pop(key, default) + def clear(self): + for value in self.values(): + self._dispose(value) + super(LRUCache, self).clear() + def _not_implemented(self, *args, **kw): raise NotImplementedError popitem = setdefault = update = _not_implemented diff --git a/attic/repository.py b/attic/repository.py index ad7031654..7b6066e0b 100755 --- a/attic/repository.py +++ b/attic/repository.py @@ -393,7 +393,8 @@ class LoggedIO(object): def __init__(self, path, limit, segments_per_dir, capacity=90): self.path = path - self.fds = LRUCache(capacity) + self.fds = LRUCache(capacity, + dispose=lambda fd: fd.close()) self.segment = 0 self.limit = limit self.segments_per_dir = segments_per_dir @@ -401,9 +402,8 @@ class LoggedIO(object): self._write_fd = None def close(self): - for segment in list(self.fds.keys()): - self.fds.pop(segment).close() self.close_segment() + self.fds.clear() self.fds = None # Just to make sure we're disabled def segment_iterator(self, reverse=False): @@ -477,9 +477,8 @@ class LoggedIO(object): return fd def delete_segment(self, segment): - fd = self.fds.pop(segment) - if fd is not None: - fd.close() + if segment in self.fds: + del self.fds[segment] try: os.unlink(self.segment_filename(segment)) except OSError: @@ -515,9 +514,8 @@ class LoggedIO(object): header = fd.read(self.header_fmt.size) def recover_segment(self, segment, filename): - fd = self.fds.pop(segment) - if fd is not None: - fd.close() + if segment in self.fds: + del self.fds[segment] # FIXME: save a copy of the original file with open(filename, 'rb') as fd: data = memoryview(fd.read()) diff --git a/attic/testsuite/lrucache.py b/attic/testsuite/lrucache.py index 9b51a7aab..60ceb41c3 100644 --- a/attic/testsuite/lrucache.py +++ b/attic/testsuite/lrucache.py @@ -5,7 +5,7 @@ from attic.testsuite import AtticTestCase class LRUCacheTestCase(AtticTestCase): def test(self): - c = LRUCache(2) + c = LRUCache(2, dispose=lambda _: None) self.assert_equal(len(c), 0) for i, x in enumerate('abc'): c[x] = i @@ -21,19 +21,13 @@ class LRUCacheTestCase(AtticTestCase): self.assert_equal(len(c), 2) self.assert_equal(c['c'], 2) self.assert_equal(c['d'], 3) - c['c'] = 22 - c['e'] = 4 - self.assert_equal(len(c), 2) - self.assert_raises(KeyError, lambda: c['d']) - self.assert_equal(c['c'], 22) - self.assert_equal(c['e'], 4) del c['c'] self.assert_equal(len(c), 1) self.assert_raises(KeyError, lambda: c['c']) - self.assert_equal(c['e'], 4) + self.assert_equal(c['d'], 3) def test_pop(self): - c = LRUCache(2) + c = LRUCache(2, dispose=lambda _: None) c[1] = 1 c[2] = 2 c.pop(1)