diff options
author | Stefan Nordhausen <nordhausen@linux-z4v3.site> | 2012-01-18 17:08:06 +0100 |
---|---|---|
committer | Stefan Nordhausen <nordhausen@linux-z4v3.site> | 2012-01-18 17:08:06 +0100 |
commit | 70d36671e8180d842e7a4237b9afa90294201667 (patch) | |
tree | e1f05d0ad1daef6929ac0a74b796293bd0719adb | |
parent | 4406c85fad0f7345953c624fbf5089f38ee9f7b2 (diff) | |
download | repoze-lru-70d36671e8180d842e7a4237b9afa90294201667.tar.gz |
Add "expiry" feature to the decorator
- Decorator now accepts "timeout" parameter.
- Unit tests check that decorator with timeout really forgets.
- Fix incorrect import in ExpiringLRUCacheTests._getTargetClass().
-rw-r--r-- | repoze/lru/__init__.py | 15 | ||||
-rwxr-xr-x | repoze/lru/tests.py | 34 |
2 files changed, 43 insertions, 6 deletions
diff --git a/repoze/lru/__init__.py b/repoze/lru/__init__.py index 938f5ba..d7bcfef 100644 --- a/repoze/lru/__init__.py +++ b/repoze/lru/__init__.py @@ -9,6 +9,8 @@ except NameError: # pragma: no cover pass _MARKER = object() +# By default, expire items after 2**60 seconds. This fits into 64 bit +# integers and is close enough to "never" for practical purposes. _DEFAULT_TIMEOUT = 2 ** 60 class LRUCache(object): @@ -249,10 +251,17 @@ class ExpiringLRUCache(object): # else: key was not in cache. Nothing to do. class lru_cache(object): - """ Decorator for LRU-cached function """ - def __init__(self, maxsize, cache=None): # cache is an arg to serve tests + """ Decorator for LRU-cached function + + timeout parameter specifies after how many seconds a cached entry should + be considered invalid. + """ + def __init__(self, maxsize, cache=None, timeout=None): # cache is an arg to serve tests if cache is None: - cache = LRUCache(maxsize) + if timeout is None: + cache = LRUCache(maxsize) + else: + cache = ExpiringLRUCache(maxsize, default_timeout=timeout) self.cache = cache def __call__(self, f): diff --git a/repoze/lru/tests.py b/repoze/lru/tests.py index a6572fb..bac2407 100755 --- a/repoze/lru/tests.py +++ b/repoze/lru/tests.py @@ -266,7 +266,7 @@ class LRUCacheTests(unittest.TestCase): class ExpiringLRUCacheTests(LRUCacheTests): def _getTargetClass(self): - from repoze.lru import LRUCache + from repoze.lru import ExpiringLRUCache return ExpiringLRUCache def _makeOne(self, size, default_timeout=None): @@ -455,8 +455,8 @@ class DecoratorTests(unittest.TestCase): from repoze.lru import lru_cache return lru_cache - def _makeOne(self, maxsize, cache): - return self._getTargetClass()(maxsize, cache) + def _makeOne(self, maxsize, cache, timeout=None): + return self._getTargetClass()(maxsize, timeout=timeout, cache=cache) def test_ctor_nocache(self): decorator = self._makeOne(10, None) @@ -492,6 +492,34 @@ class DecoratorTests(unittest.TestCase): self.assertEqual(result, (3, 4, 5)) self.assertEqual(len(cache), 1) + def test_expiry(self): + """When timeout is given, decorator must eventually forget entries""" + @self._makeOne(1, None, timeout=0.1) + def sleep_a_bit(param): + time.sleep(0.1) + return 2 * param + + # First call must take at least 0.1 seconds + start = time.time() + result1 = sleep_a_bit("hello") + stop = time.time() + self.assertEqual(result1, 2 * "hello") + self.assertGreater(stop - start, 0.1) + + # Second call must take less than 0.1 seconds. + start = time.time() + result2 = sleep_a_bit("hello") + stop = time.time() + self.assertEqual(result2, 2 * "hello") + self.assertLess(stop - start, 0.1) + + time.sleep(0.1) + # This one must calculate again and take at least 0.1 seconds + start = time.time() + result3 = sleep_a_bit("hello") + stop = time.time() + self.assertEqual(result3, 2 * "hello") + self.assertGreater(stop - start, 0.1) class DummyLRUCache(dict): def put(self, k, v): |