mirror of
https://github.com/wassname/catalyst.git
synced 2026-07-21 12:30:16 +08:00
ENH: Add expiring cache.
Add a cache interface which supports expirable entries with a changeable
backend for the cache into which they are entered.
The default cache is a `dict` but could swapped for
`cachetools.LRUCache` or any other cache which supports `__get__`,
`__set__`, and `__del__`.
So that consumers can change the use of `CachedObjects` stored in a
cache from:
```
self._cache = {}
...
try:
obj = self._cache[key]
try:
return obj.unwrap(dt)
except Expired:
pass
except KeyError:
pass
...
self._cache[key] = CachedObject(value, new_expiration)
```
to:
```
self._cache = ExpiringCache(LRUCache(maxsize=6))
...
try:
return self._cache.get(key, dt)
except KeyError:
# Get fresh value
...
self._cache.set(key, value, new_expiration)
```
This commit is contained in:
@@ -2,7 +2,7 @@ from unittest import TestCase
|
||||
|
||||
from pandas import Timestamp, Timedelta
|
||||
|
||||
from zipline.utils.cache import CachedObject, Expired
|
||||
from zipline.utils.cache import CachedObject, Expired, ExpiringCache
|
||||
|
||||
|
||||
class CachedObjectTestCase(TestCase):
|
||||
@@ -19,3 +19,42 @@ class CachedObjectTestCase(TestCase):
|
||||
with self.assertRaises(Expired) as e:
|
||||
obj.unwrap(after)
|
||||
self.assertEqual(e.exception.args, (expiry,))
|
||||
|
||||
|
||||
class ExpiringCacheTestCase(TestCase):
|
||||
|
||||
def test_expiring_cache(self):
|
||||
expiry_1 = Timestamp('2014')
|
||||
before_1 = expiry_1 - Timedelta('1 minute')
|
||||
after_1 = expiry_1 + Timedelta('1 minute')
|
||||
|
||||
expiry_2 = Timestamp('2015')
|
||||
after_2 = expiry_1 + Timedelta('1 minute')
|
||||
|
||||
expiry_3 = Timestamp('2016')
|
||||
|
||||
cache = ExpiringCache()
|
||||
|
||||
cache.set('foo', 1, expiry_1)
|
||||
cache.set('bar', 2, expiry_2)
|
||||
|
||||
self.assertEqual(cache.get('foo', before_1), 1)
|
||||
# Unwrap on expiry is allowed.
|
||||
self.assertEqual(cache.get('foo', expiry_1), 1)
|
||||
|
||||
with self.assertRaises(KeyError) as e:
|
||||
self.assertEqual(cache.get('foo', after_1))
|
||||
self.assertEqual(e.exception.args, ('foo',))
|
||||
|
||||
# Should raise same KeyError after deletion.
|
||||
with self.assertRaises(KeyError) as e:
|
||||
self.assertEqual(cache.get('foo', before_1))
|
||||
self.assertEqual(e.exception.args, ('foo',))
|
||||
|
||||
# Second value should still exist.
|
||||
self.assertEqual(cache.get('bar', after_2), 2)
|
||||
|
||||
# Should raise similar KeyError on non-existent key.
|
||||
with self.assertRaises(KeyError) as e:
|
||||
self.assertEqual(cache.get('baz', expiry_3))
|
||||
self.assertEqual(e.exception.args, ('baz',))
|
||||
|
||||
Reference in New Issue
Block a user