2
# Copyright 2009-2010 Joshua Roesslein
3
# See LICENSE for details.
8
import cPickle as pickle
19
# Probably on a windows system
27
def __init__(self, timeout=60):
28
"""Initialize the cache
29
timeout: number of seconds to keep a cached entry
31
self.timeout = timeout
33
def store(self, key, value):
34
"""Add new record to cache
38
raise NotImplementedError
40
def get(self, key, timeout=None):
41
"""Get cached entry if exists and not expired
42
key: which entry to get
43
timeout: override timeout with this value [optional]
45
raise NotImplementedError
48
"""Get count of entries currently stored in cache"""
49
raise NotImplementedError
52
"""Delete any expired entries in cache."""
53
raise NotImplementedError
56
"""Delete all cached entries"""
57
raise NotImplementedError
60
class MemoryCache(Cache):
63
def __init__(self, timeout=60):
64
Cache.__init__(self, timeout)
66
self.lock = threading.Lock()
68
def __getstate__(self):
70
return {'entries': self._entries, 'timeout': self.timeout}
72
def __setstate__(self, state):
74
self.lock = threading.Lock()
75
self._entries = state['entries']
76
self.timeout = state['timeout']
78
def _is_expired(self, entry, timeout):
79
return timeout > 0 and (time.time() - entry[0]) >= timeout
81
def store(self, key, value):
83
self._entries[key] = (time.time(), value)
86
def get(self, key, timeout=None):
89
# check to see if we have this key
90
entry = self._entries.get(key)
92
# no hit, return nothing
95
# use provided timeout in arguments if provided
96
# otherwise use the one provided during init.
98
timeout = self.timeout
100
# make sure entry is not expired
101
if self._is_expired(entry, timeout):
102
# entry expired, delete and return nothing
103
del self._entries[key]
106
# entry found and not expired, return it
112
return len(self._entries)
117
for k, v in self._entries.items():
118
if self._is_expired(v, self.timeout):
125
self._entries.clear()
129
class FileCache(Cache):
130
"""File-based cache"""
132
# locks used to make cache thread-safe
135
def __init__(self, cache_dir, timeout=60):
136
Cache.__init__(self, timeout)
137
if os.path.exists(cache_dir) is False:
139
self.cache_dir = cache_dir
140
if cache_dir in FileCache.cache_locks:
141
self.lock = FileCache.cache_locks[cache_dir]
143
self.lock = threading.Lock()
144
FileCache.cache_locks[cache_dir] = self.lock
146
if os.name == 'posix':
147
self._lock_file = self._lock_file_posix
148
self._unlock_file = self._unlock_file_posix
149
elif os.name == 'nt':
150
self._lock_file = self._lock_file_win32
151
self._unlock_file = self._unlock_file_win32
153
print 'Warning! FileCache locking not supported on this system!'
154
self._lock_file = self._lock_file_dummy
155
self._unlock_file = self._unlock_file_dummy
157
def _get_path(self, key):
160
return os.path.join(self.cache_dir, md5.hexdigest())
162
def _lock_file_dummy(self, path, exclusive=True):
165
def _unlock_file_dummy(self, lock):
168
def _lock_file_posix(self, path, exclusive=True):
169
lock_path = path + '.lock'
170
if exclusive is True:
171
f_lock = open(lock_path, 'w')
172
fcntl.lockf(f_lock, fcntl.LOCK_EX)
174
f_lock = open(lock_path, 'r')
175
fcntl.lockf(f_lock, fcntl.LOCK_SH)
176
if os.path.exists(lock_path) is False:
181
def _unlock_file_posix(self, lock):
184
def _lock_file_win32(self, path, exclusive=True):
188
def _unlock_file_win32(self, lock):
192
def _delete_file(self, path):
194
if os.path.exists(path + '.lock'):
195
os.remove(path + '.lock')
197
def store(self, key, value):
198
path = self._get_path(key)
201
# acquire lock and open file
202
f_lock = self._lock_file(path)
203
datafile = open(path, 'wb')
206
pickle.dump((time.time(), value), datafile)
208
# close and unlock file
210
self._unlock_file(f_lock)
214
def get(self, key, timeout=None):
215
return self._get(self._get_path(key), timeout)
217
def _get(self, path, timeout):
218
if os.path.exists(path) is False:
223
# acquire lock and open
224
f_lock = self._lock_file(path, False)
225
datafile = open(path, 'rb')
227
# read pickled object
228
created_time, value = pickle.load(datafile)
231
# check if value is expired
233
timeout = self.timeout
234
if timeout > 0 and (time.time() - created_time) >= timeout:
235
# expired! delete from cache
237
self._delete_file(path)
239
# unlock and return result
240
self._unlock_file(f_lock)
247
for entry in os.listdir(self.cache_dir):
248
if entry.endswith('.lock'):
254
for entry in os.listdir(self.cache_dir):
255
if entry.endswith('.lock'):
257
self._get(os.path.join(self.cache_dir, entry), None)
260
for entry in os.listdir(self.cache_dir):
261
if entry.endswith('.lock'):
263
self._delete_file(os.path.join(self.cache_dir, entry))