Changeset 6887
- Timestamp:
- 12/04/07 12:03:56 (9 months ago)
- Files:
-
- django/trunk/django/core/cache/backends/filebased.py (modified) (7 diffs)
- django/trunk/tests/regressiontests/cache/tests.py (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/trunk/django/core/cache/backends/filebased.py
r6822 r6887 1 1 "File-based cache backend" 2 2 3 import md5 3 4 import os, time 4 5 try: … … 7 8 import pickle 8 9 from django.core.cache.backends.base import BaseCache 9 from django.utils.http import urlquote_plus10 10 11 11 class CacheClass(BaseCache): … … 30 30 31 31 def add(self, key, value, timeout=None): 32 fname = self._key_to_file(key) 33 if timeout is None: 34 timeout = self.default_timeout 35 try: 36 filelist = os.listdir(self._dir) 37 except (IOError, OSError): 38 self._createdir() 39 filelist = [] 40 if len(filelist) > self._max_entries: 41 self._cull(filelist) 42 if os.path.basename(fname) not in filelist: 43 try: 44 f = open(fname, 'wb') 45 now = time.time() 46 pickle.dump(now + timeout, f, 2) 47 pickle.dump(value, f, 2) 48 except (IOError, OSError): 49 pass 32 if self.has_key(key): 33 return None 34 35 self.set(key, value, timeout) 50 36 51 37 def get(self, key, default=None): … … 57 43 if exp < now: 58 44 f.close() 59 os.remove(fname)45 self._delete(fname) 60 46 else: 61 47 return pickle.load(f) … … 66 52 def set(self, key, value, timeout=None): 67 53 fname = self._key_to_file(key) 54 dirname = os.path.dirname(fname) 55 68 56 if timeout is None: 69 57 timeout = self.default_timeout 58 59 self._cull() 60 70 61 try: 71 filelist = os.listdir(self._dir) 72 except (IOError, OSError): 73 self._createdir() 74 filelist = [] 75 if len(filelist) > self._max_entries: 76 self._cull(filelist) 77 try: 62 if not os.path.exists(dirname): 63 os.makedirs(dirname) 64 78 65 f = open(fname, 'wb') 79 66 now = time.time() 80 pickle.dump(now + timeout, f, 2)81 pickle.dump(value, f, 2)67 pickle.dump(now + timeout, f, pickle.HIGHEST_PROTOCOL) 68 pickle.dump(value, f, pickle.HIGHEST_PROTOCOL) 82 69 except (IOError, OSError): 83 70 pass … … 85 72 def delete(self, key): 86 73 try: 87 os.remove(self._key_to_file(key)) 74 self._delete(self._key_to_file(key)) 75 except (IOError, OSError): 76 pass 77 78 def _delete(self, fname): 79 os.remove(fname) 80 try: 81 # Remove the 2 subdirs if they're empty 82 dirname = os.path.dirname(fname) 83 os.rmdir(dirname) 84 os.rmdir(os.path.dirname(dirname)) 88 85 except (IOError, OSError): 89 86 pass 90 87 91 88 def has_key(self, key): 92 return os.path.exists(self._key_to_file(key)) 89 fname = self._key_to_file(key) 90 try: 91 f = open(fname, 'rb') 92 exp = pickle.load(f) 93 now = time.time() 94 if exp < now: 95 f.close() 96 self._delete(fname) 97 return False 98 else: 99 return True 100 except (IOError, OSError, EOFError, pickle.PickleError): 101 return False 93 102 94 def _cull(self, filelist): 103 def _cull(self): 104 if int(self._num_entries) < self._max_entries: 105 return 106 107 try: 108 filelist = os.listdir(self._dir) 109 except (IOError, OSError): 110 return 111 95 112 if self._cull_frequency == 0: 96 113 doomed = filelist 97 114 else: 98 doomed = [k for (i, k) in enumerate(filelist) if i % self._cull_frequency == 0] 99 for fname in doomed: 115 doomed = [os.path.join(self._dir, k) for (i, k) in enumerate(filelist) if i % self._cull_frequency == 0] 116 117 for topdir in doomed: 100 118 try: 101 os.remove(os.path.join(self._dir, fname)) 119 for root, _, files in os.walk(topdir): 120 for f in files: 121 self._delete(os.path.join(root, f)) 102 122 except (IOError, OSError): 103 123 pass … … 110 130 111 131 def _key_to_file(self, key): 112 return os.path.join(self._dir, urlquote_plus(key)) 132 """ 133 Convert the filename into an md5 string. We'll turn the first couple 134 bits of the path into directory prefixes to be nice to filesystems 135 that have problems with large numbers of files in a directory. 136 137 Thus, a cache key of "foo" gets turnned into a file named 138 ``{cache-dir}ac/bd/18db4cc2f85cedef654fccc4a4d8``. 139 """ 140 path = md5.new(key.encode('utf-8')).hexdigest() 141 path = os.path.join(path[:2], path[2:4], path[4:]) 142 return os.path.join(self._dir, path) 143 144 def _get_num_entries(self): 145 count = 0 146 for _,_,files in os.walk(self._dir): 147 count += len(files) 148 return count 149 _num_entries = property(_get_num_entries) 150 django/trunk/tests/regressiontests/cache/tests.py
r6822 r6887 4 4 # Uses whatever cache backend is set in the test settings file. 5 5 6 import time , unittest7 6 import time 7 import unittest 8 8 from django.core.cache import cache 9 9 from django.utils.cache import patch_vary_headers … … 28 28 cache.add("addkey1", "newvalue") 29 29 self.assertEqual(cache.get("addkey1"), "value") 30 30 31 31 def test_non_existent(self): 32 32 # get with non-existent keys … … 77 77 78 78 def test_expiration(self): 79 # expiration 80 cache.set('expire', 'very quickly', 1) 81 time.sleep(2) 82 self.assertEqual(cache.get("expire"), None) 79 cache.set('expire1', 'very quickly', 1) 80 cache.set('expire2', 'very quickly', 1) 81 cache.set('expire3', 'very quickly', 1) 82 83 time.sleep(2) 84 self.assertEqual(cache.get("expire1"), None) 85 86 cache.add("expire2", "newvalue") 87 self.assertEqual(cache.get("expire2"), "newvalue") 88 self.assertEqual(cache.has_key("expire3"), False) 83 89 84 90 def test_unicode(self): … … 93 99 self.assertEqual(cache.get(key), value) 94 100 101 import os 102 import md5 103 import shutil 104 import tempfile 105 from django.core.cache.backends.filebased import CacheClass as FileCache 106 107 class FileBasedCacheTests(unittest.TestCase): 108 """ 109 Specific test cases for the file-based cache. 110 """ 111 def setUp(self): 112 self.dirname = tempfile.mktemp() 113 os.mkdir(self.dirname) 114 self.cache = FileCache(self.dirname, {}) 115 116 def tearDown(self): 117 shutil.rmtree(self.dirname) 118 119 def test_hashing(self): 120 """Test that keys are hashed into subdirectories correctly""" 121 self.cache.set("foo", "bar") 122 keyhash = md5.new("foo").hexdigest() 123 keypath = os.path.join(self.dirname, keyhash[:2], keyhash[2:4], keyhash[4:]) 124 self.assert_(os.path.exists(keypath)) 125 126 def test_subdirectory_removal(self): 127 """ 128 Make sure that the created subdirectories are correctly removed when empty. 129 """ 130 self.cache.set("foo", "bar") 131 keyhash = md5.new("foo").hexdigest() 132 keypath = os.path.join(self.dirname, keyhash[:2], keyhash[2:4], keyhash[4:]) 133 self.assert_(os.path.exists(keypath)) 134 135 self.cache.delete("foo") 136 self.assert_(not os.path.exists(keypath)) 137 self.assert_(not os.path.exists(os.path.dirname(keypath))) 138 self.assert_(not os.path.exists(os.path.dirname(os.path.dirname(keypath)))) 95 139 96 140 class CacheUtils(unittest.TestCase):
