1
2
3 '''Call result cache.
4
5 Designed to have the same interface as the `Django low-level cache API`_.
6 Heavily inspired (read: mostly copied-and-pasted) from the Django framework -
7 thanks to those guys for designing a simple and effective cache!
8
9 .. _`Django low-level cache API`: http://www.djangoproject.com/documentation/cache/#the-low-level-cache-api
10 '''
11
12 import threading
13 import time
17 '''Simple response cache for FlickrAPI calls.
18
19 This stores max 50 entries, timing them out after 120 seconds:
20 >>> cache = SimpleCache(timeout=120, max_entries=50)
21 '''
22
23 - def __init__(self, timeout=300, max_entries=200):
24 self.storage = {}
25 self.expire_info = {}
26 self.lock = threading.RLock()
27 self.default_timeout = timeout
28 self.max_entries = max_entries
29 self.cull_frequency = 3
30
32 '''Method decorator, ensures the method call is locked'''
33
34 def locked(self, *args, **kwargs):
35 self.lock.acquire()
36 try:
37 return method(self, *args, **kwargs)
38 finally:
39 self.lock.release()
40
41 return locked
42
43 @locking
44 - def get(self, key, default=None):
45 '''Fetch a given key from the cache. If the key does not exist, return
46 default, which itself defaults to None.
47 '''
48
49 now = time.time()
50 exp = self.expire_info.get(key)
51 if exp is None:
52 return default
53 elif exp < now:
54 self.delete(key)
55 return default
56
57 return self.storage[key]
58
59 @locking
60 - def set(self, key, value, timeout=None):
61 '''Set a value in the cache. If timeout is given, that timeout will be
62 used for the key; otherwise the default cache timeout will be used.
63 '''
64
65 if len(self.storage) >= self.max_entries:
66 self.cull()
67 if timeout is None:
68 timeout = self.default_timeout
69 self.storage[key] = value
70 self.expire_info[key] = time.time() + timeout
71
72 @locking
74 '''Deletes a key from the cache,
75 failing silently if it doesn't exist.'''
76
77 if key in self.storage:
78 del self.storage[key]
79 if key in self.expire_info:
80 del self.expire_info[key]
81
82 @locking
84 '''Returns True if the key is in the cache and has not expired.'''
85 return self.get(key) is not None
86
87 @locking
89 '''Returns True if the key is in the cache and has not expired.'''
90 return self.has_key(key)
91
92 @locking
94 '''Reduces the number of cached items'''
95
96 doomed = [k for (i, k) in enumerate(self.storage)
97 if i % self.cull_frequency == 0]
98 for k in doomed:
99 self.delete(k)
100
101 @locking
103 '''Returns the number of cached items -- they might be expired
104 though.
105 '''
106
107 return len(self.storage)
108