| | 226 | ############################### |
|---|
| | 227 | # Thread-safe in-memory cache # |
|---|
| | 228 | ############################### |
|---|
| | 229 | |
|---|
| | 230 | try: |
|---|
| | 231 | import cPickle as pickle |
|---|
| | 232 | except ImportError: |
|---|
| | 233 | import pickle |
|---|
| | 234 | from django.utils.synch import RWLock |
|---|
| | 235 | |
|---|
| | 236 | class _LocMemCache(_SimpleCache): |
|---|
| | 237 | """Thread-safe in-memory cache""" |
|---|
| | 238 | |
|---|
| | 239 | def __init__(self, host, params): |
|---|
| | 240 | _SimpleCache.__init__(self, host, params) |
|---|
| | 241 | self._lock = RWLock() |
|---|
| | 242 | |
|---|
| | 243 | def get(self, key, default=None): |
|---|
| | 244 | should_delete = False |
|---|
| | 245 | self._lock.reader_enters() |
|---|
| | 246 | try: |
|---|
| | 247 | now = time.time() |
|---|
| | 248 | exp = self._expire_info.get(key) |
|---|
| | 249 | if exp is None: |
|---|
| | 250 | return default |
|---|
| | 251 | elif exp < now: |
|---|
| | 252 | should_delete = True |
|---|
| | 253 | else: |
|---|
| | 254 | return self._cache[key] |
|---|
| | 255 | finally: |
|---|
| | 256 | self._lock.reader_leaves() |
|---|
| | 257 | if should_delete: |
|---|
| | 258 | self._lock.writer_enters() |
|---|
| | 259 | try: |
|---|
| | 260 | del self._cache[key] |
|---|
| | 261 | del self._expire_info[key] |
|---|
| | 262 | return default |
|---|
| | 263 | finally: |
|---|
| | 264 | self._lock.writer_leaves() |
|---|
| | 265 | |
|---|
| | 266 | def set(self, key, value, timeout=None): |
|---|
| | 267 | self._lock.writer_enters() |
|---|
| | 268 | try: |
|---|
| | 269 | _SimpleCache.set(self, key, value, timeout) |
|---|
| | 270 | finally: |
|---|
| | 271 | self._lock.writer_leaves() |
|---|
| | 272 | |
|---|
| | 273 | def delete(self, key): |
|---|
| | 274 | self._lock.writer_enters() |
|---|
| | 275 | try: |
|---|
| | 276 | _SimpleCache.delete(self, key) |
|---|
| | 277 | finally: |
|---|
| | 278 | self._lock.writer_leaves() |
|---|
| | 279 | |
|---|
| | 280 | #################### |
|---|
| | 281 | # File-based cache # |
|---|
| | 282 | #################### |
|---|
| | 283 | |
|---|
| | 284 | import os |
|---|
| | 285 | import urllib |
|---|
| | 286 | |
|---|
| | 287 | class _FileCache(_SimpleCache): |
|---|
| | 288 | """File-based cache""" |
|---|
| | 289 | |
|---|
| | 290 | def __init__(self, dir, params): |
|---|
| | 291 | self._dir = dir |
|---|
| | 292 | if not os.path.exists(self._dir): |
|---|
| | 293 | try: |
|---|
| | 294 | os.makedirs(self._dir) |
|---|
| | 295 | except OSError: |
|---|
| | 296 | raise EnvironmentError, "Cache directory '%s' does not exist and could not be created'" % self._dir |
|---|
| | 297 | _SimpleCache.__init__(self, dir, params) |
|---|
| | 298 | del self._cache |
|---|
| | 299 | del self._expire_info |
|---|
| | 300 | |
|---|
| | 301 | def get(self, key, default=None): |
|---|
| | 302 | fname = self._key_to_file(key) |
|---|
| | 303 | try: |
|---|
| | 304 | f = open(fname, 'rb') |
|---|
| | 305 | exp = pickle.load(f) |
|---|
| | 306 | now = time.time() |
|---|
| | 307 | if exp < now: |
|---|
| | 308 | f.close() |
|---|
| | 309 | os.remove(fname) |
|---|
| | 310 | else: |
|---|
| | 311 | return pickle.load(f) |
|---|
| | 312 | except (IOError, pickle.PickleError): |
|---|
| | 313 | pass |
|---|
| | 314 | return default |
|---|
| | 315 | |
|---|
| | 316 | def set(self, key, value, timeout=None): |
|---|
| | 317 | fname = self._key_to_file(key) |
|---|
| | 318 | if timeout is None: |
|---|
| | 319 | timeout = self.default_timeout |
|---|
| | 320 | filelist = os.listdir(self._dir) |
|---|
| | 321 | if len(filelist) > self._max_entries: |
|---|
| | 322 | self._cull(filelist) |
|---|
| | 323 | try: |
|---|
| | 324 | f = open(fname, 'wb') |
|---|
| | 325 | now = time.time() |
|---|
| | 326 | pickle.dump(now + timeout, f, 2) |
|---|
| | 327 | pickle.dump(value, f, 2) |
|---|
| | 328 | except (IOError, OSError): |
|---|
| | 329 | raise |
|---|
| | 330 | |
|---|
| | 331 | def delete(self, key): |
|---|
| | 332 | try: |
|---|
| | 333 | os.remove(self._key_to_file(key)) |
|---|
| | 334 | except (IOError, OSError): |
|---|
| | 335 | pass |
|---|
| | 336 | |
|---|
| | 337 | def has_key(self, key): |
|---|
| | 338 | return os.path.exists(self._key_to_file(key)) |
|---|
| | 339 | |
|---|
| | 340 | def _cull(self, filelist): |
|---|
| | 341 | if self.cull_frequency == 0: |
|---|
| | 342 | doomed = filelist |
|---|
| | 343 | else: |
|---|
| | 344 | doomed = [k for (i, k) in enumerate(filelist) if i % self._cull_frequency == 0] |
|---|
| | 345 | for fname in doomed: |
|---|
| | 346 | try: |
|---|
| | 347 | os.remove(os.path.join(self._dir, fname)) |
|---|
| | 348 | except (IOError, OSError): |
|---|
| | 349 | pass |
|---|
| | 350 | |
|---|
| | 351 | def _key_to_file(self, key): |
|---|
| | 352 | return os.path.join(self._dir, urllib.quote_plus(key)) |
|---|
| | 353 | |
|---|