-
Notifications
You must be signed in to change notification settings - Fork 138
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement Django-compatible cache and add testing
- Loading branch information
1 parent
b08a090
commit a59bd8b
Showing
12 changed files
with
1,135 additions
and
190 deletions.
There are no files selected for viewing
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,192 +1,54 @@ | ||
"Django-compatible disk and file-based cache." | ||
|
||
from django.core.cache.backends.base import DEFAULT_TIMEOUT, BaseCache | ||
# from django.core.files.move import file_move_safe | ||
# from django.utils.encoding import force_bytes | ||
|
||
# try: | ||
# from django.utils.six.moves import cPickle as pickle | ||
# except ImportError: | ||
# import pickle | ||
|
||
from .core import Cache | ||
|
||
|
||
class DjangoCache(BaseCache): | ||
"Disk and file-based cache compatible with Django." | ||
|
||
"Django-compatible disk and file-based cache." | ||
def __init__(self, directory, params): | ||
super(DjangoCache, self).__init__(params) | ||
|
||
self._cache = Cache(directory) | ||
|
||
|
||
def add(self, key, value, timeout=DEFAULT_TIMEOUT, version=None): | ||
pass | ||
|
||
add.__doc__ = BaseCache.add.__doc__ | ||
|
||
if self.has_key(key, version): | ||
return False | ||
self.set(key, value, timeout, version) | ||
return True | ||
|
||
def get(self, key, default=None, version=None): | ||
pass | ||
|
||
get.__doc__ = BaseCache.get.__doc__ | ||
|
||
key = self.make_key(key, version=version) | ||
self.validate_key(key) | ||
return self._cache.get(key, default=default) | ||
|
||
def set(self, key, value, timeout=DEFAULT_TIMEOUT, version=None): | ||
pass | ||
|
||
set.__doc__ = BaseCache.set.__doc__ | ||
|
||
key = self.make_key(key, version=version) | ||
self.validate_key(key) | ||
timeout = self.get_backend_timeout(timeout=timeout) | ||
self._cache.set(key, value, expire=timeout) | ||
|
||
def delete(self, key, version=None): | ||
pass | ||
|
||
delete.__doc__ = BaseCache.delete.__doc__ | ||
key = self.make_key(key, version=version) | ||
self.validate_key(key) | ||
self._cache.delete(key) | ||
|
||
def has_key(self, key, version=None): | ||
key = self.make_key(key, version=version) | ||
self.validate_key(key) | ||
return key in self._cache | ||
|
||
def clear(self): | ||
pass | ||
self._cache.clear() | ||
|
||
clear.__doc__ = BaseCache.clear.__doc__ | ||
|
||
|
||
def close(self, **kwargs): | ||
def close(self): | ||
self._cache.close() | ||
|
||
close.__doc__ = BaseCache.close.__doc__ | ||
|
||
|
||
# class FileBasedCache(BaseCache): | ||
# cache_suffix = '.djcache' | ||
|
||
# def __init__(self, dir, params): | ||
# super(FileBasedCache, self).__init__(params) | ||
# self._dir = os.path.abspath(dir) | ||
# self._createdir() | ||
|
||
# def add(self, key, value, timeout=DEFAULT_TIMEOUT, version=None): | ||
# if self.has_key(key, version): | ||
# return False | ||
# self.set(key, value, timeout, version) | ||
# return True | ||
|
||
# def get(self, key, default=None, version=None): | ||
# fname = self._key_to_file(key, version) | ||
# if os.path.exists(fname): | ||
# try: | ||
# with io.open(fname, 'rb') as f: | ||
# if not self._is_expired(f): | ||
# return pickle.loads(zlib.decompress(f.read())) | ||
# except IOError as e: | ||
# if e.errno == errno.ENOENT: | ||
# pass # Cache file was removed after the exists check | ||
# return default | ||
|
||
# def set(self, key, value, timeout=DEFAULT_TIMEOUT, version=None): | ||
# self._createdir() # Cache dir can be deleted at any time. | ||
# fname = self._key_to_file(key, version) | ||
# self._cull() # make some room if necessary | ||
# fd, tmp_path = tempfile.mkstemp(dir=self._dir) | ||
# renamed = False | ||
# try: | ||
# with io.open(fd, 'wb') as f: | ||
# expiry = self.get_backend_timeout(timeout) | ||
# f.write(pickle.dumps(expiry, pickle.HIGHEST_PROTOCOL)) | ||
# f.write(zlib.compress(pickle.dumps(value, pickle.HIGHEST_PROTOCOL))) | ||
# file_move_safe(tmp_path, fname, allow_overwrite=True) | ||
# renamed = True | ||
# finally: | ||
# if not renamed: | ||
# os.remove(tmp_path) | ||
|
||
# def delete(self, key, version=None): | ||
# self._delete(self._key_to_file(key, version)) | ||
|
||
# def _delete(self, fname): | ||
# if not fname.startswith(self._dir) or not os.path.exists(fname): | ||
# return | ||
# try: | ||
# os.remove(fname) | ||
# except OSError as e: | ||
# # ENOENT can happen if the cache file is removed (by another | ||
# # process) after the os.path.exists check. | ||
# if e.errno != errno.ENOENT: | ||
# raise | ||
|
||
# def has_key(self, key, version=None): | ||
# fname = self._key_to_file(key, version) | ||
# if os.path.exists(fname): | ||
# with io.open(fname, 'rb') as f: | ||
# return not self._is_expired(f) | ||
# return False | ||
|
||
# def _cull(self): | ||
# """ | ||
# Removes random cache entries if max_entries is reached at a ratio | ||
# of num_entries / cull_frequency. A value of 0 for CULL_FREQUENCY means | ||
# that the entire cache will be purged. | ||
# """ | ||
# filelist = self._list_cache_files() | ||
# num_entries = len(filelist) | ||
# if num_entries < self._max_entries: | ||
# return # return early if no culling is required | ||
# if self._cull_frequency == 0: | ||
# return self.clear() # Clear the cache when CULL_FREQUENCY = 0 | ||
# # Delete a random selection of entries | ||
# filelist = random.sample(filelist, | ||
# int(num_entries / self._cull_frequency)) | ||
# for fname in filelist: | ||
# self._delete(fname) | ||
|
||
# def _createdir(self): | ||
# if not os.path.exists(self._dir): | ||
# try: | ||
# os.makedirs(self._dir, 0o700) | ||
# except OSError as e: | ||
# if e.errno != errno.EEXIST: | ||
# raise EnvironmentError( | ||
# "Cache directory '%s' does not exist " | ||
# "and could not be created'" % self._dir) | ||
|
||
# def _key_to_file(self, key, version=None): | ||
# """ | ||
# Convert a key into a cache file path. Basically this is the | ||
# root cache path joined with the md5sum of the key and a suffix. | ||
# """ | ||
# key = self.make_key(key, version=version) | ||
# self.validate_key(key) | ||
# return os.path.join(self._dir, ''.join( | ||
# [hashlib.md5(force_bytes(key)).hexdigest(), self.cache_suffix])) | ||
|
||
# def clear(self): | ||
# """ | ||
# Remove all the cache files. | ||
# """ | ||
# if not os.path.exists(self._dir): | ||
# return | ||
# for fname in self._list_cache_files(): | ||
# self._delete(fname) | ||
|
||
# def _is_expired(self, f): | ||
# """ | ||
# Takes an open cache file and determines if it has expired, | ||
# deletes the file if it is has passed its expiry time. | ||
# """ | ||
# exp = pickle.load(f) | ||
# if exp is not None and exp < time.time(): | ||
# f.close() # On Windows a file has to be closed before deleting | ||
# self._delete(f.name) | ||
# return True | ||
# return False | ||
|
||
# def _list_cache_files(self): | ||
# """ | ||
# Get a list of paths to all the cache files. These are all the files | ||
# in the root cache dir that end on the cache_suffix. | ||
# """ | ||
# if not os.path.exists(self._dir): | ||
# return [] | ||
# filelist = [os.path.join(self._dir, fname) for fname | ||
# in glob.glob1(self._dir, '*%s' % self.cache_suffix)] | ||
# return filelist | ||
def get_backend_timeout(self, timeout=DEFAULT_TIMEOUT): | ||
"Return seconds to expiration." | ||
if timeout == DEFAULT_TIMEOUT: | ||
timeout = self.default_timeout | ||
elif timeout == 0: | ||
# ticket 21147 - avoid time.time() related precision issues | ||
timeout = -1 | ||
return None if timeout is None else timeout |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
mock==1.3.0 | ||
nose==1.3.7 | ||
statistics==1.0.3.5 | ||
django==1.9.2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import os | ||
|
||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tests.settings') | ||
|
||
import django | ||
|
||
django.setup() | ||
|
||
from django.db import models | ||
from django.utils import timezone | ||
|
||
|
||
def expensive_calculation(): | ||
expensive_calculation.num_runs += 1 | ||
return timezone.now() | ||
|
||
|
||
class Poll(models.Model): | ||
question = models.CharField(max_length=200) | ||
answer = models.CharField(max_length=200) | ||
pub_date = models.DateTimeField('date published', default=expensive_calculation) |
Oops, something went wrong.