Django

Code

Ticket #6099: filebased_md5.patch

File filebased_md5.patch, 7.1 kB (added by sherbang, 11 months ago)
  • a/django/core/cache/backends/filebased.py

    old new  
    11"File-based cache backend" 
    22 
     3import md5 
    34import os, time 
    45try: 
    56    import cPickle as pickle 
    67except ImportError: 
    78    import pickle 
    89from django.core.cache.backends.base import BaseCache 
    9 from django.utils.http import urlquote_plus 
     10 
     11class EntryCount: 
     12    #TODO: I assume it would be a worthwhile performance improvement (on large caches) 
     13    #to keep a count in a file, and use that here instead of recounting everytime. 
     14    # 
     15    #The hooks are all in place below to allow that just implement  
     16    #increment/decrement/__int__ here, as you see fit. 
     17    def __init__(self, dir): 
     18        self._dir = dir 
     19         
     20    def increment(self): 
     21        pass 
     22    def decrement(self): 
     23        pass 
     24    def __int__(self): 
     25        count = 0 
     26        for _,_,files in os.walk(self._dir): 
     27            count += len(files) 
     28        return count 
     29             
    1030 
    1131class CacheClass(BaseCache): 
    1232    def __init__(self, dir, params): 
     
    2747        self._dir = dir 
    2848        if not os.path.exists(self._dir): 
    2949            self._createdir() 
     50             
     51        self._num_entries = EntryCount(self._dir) 
    3052 
    3153    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 
     54        if self.has_key(key): 
     55            return None 
     56         
     57        self.set(key, value, timeout) 
    5058 
    5159    def get(self, key, default=None): 
    5260        fname = self._key_to_file(key) 
     
    5664            now = time.time() 
    5765            if exp < now: 
    5866                f.close() 
    59                 os.remove(fname) 
     67                self._delete(fname) 
    6068            else: 
    6169                return pickle.load(f) 
    6270        except (IOError, OSError, EOFError, pickle.PickleError): 
     
    6573 
    6674    def set(self, key, value, timeout=None): 
    6775        fname = self._key_to_file(key) 
     76        dir = os.path.dirname(fname) 
     77         
    6878        if timeout is None: 
    6979            timeout = self.default_timeout 
     80             
     81        self._cull() 
     82         
    7083        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: 
     84            if not os.path.exists(dir): 
     85                os.makedirs(dir) 
     86 
    7887            f = open(fname, 'wb') 
    7988            now = time.time() 
    8089            pickle.dump(now + timeout, f, 2) 
    8190            pickle.dump(value, f, 2) 
     91            self._num_entries.increment() 
    8292        except (IOError, OSError): 
    8393            pass 
    8494 
    8595    def delete(self, key): 
    8696        try: 
    87             os.remove(self._key_to_file(key)) 
     97            self._delete(self._key_to_file(key)) 
    8898        except (IOError, OSError): 
    8999            pass 
    90100 
     101    def _delete(self, file): 
     102        os.remove(file) 
     103        self._num_entries.decrement() 
     104        try: 
     105            #remove the 2 subdirs if they're empty 
     106            dir = os.path.dirname(file) 
     107            os.rmdir(dir) 
     108            os.rmdir(os.path.dirname(dir)) 
     109        except: 
     110            pass 
     111 
    91112    def has_key(self, key): 
    92         return os.path.exists(self._key_to_file(key)) 
     113        fname = self._key_to_file(key) 
     114        try: 
     115            f = open(fname, 'rb') 
     116            exp = pickle.load(f) 
     117            now = time.time() 
     118            if exp < now: 
     119                f.close() 
     120                self._delete(fname) 
     121                return False 
     122            else: 
     123                return True 
     124        except (IOError, OSError, EOFError, pickle.PickleError): 
     125            return False 
    93126 
    94     def _cull(self, filelist): 
     127    def _cull(self): 
     128        if int(self._num_entries) < self._max_entries: 
     129            return 
     130         
     131        try: 
     132            filelist = os.listdir(self._dir) 
     133        except (IOError, OSError): 
     134            return 
     135         
    95136        if self._cull_frequency == 0: 
    96137            doomed = filelist 
    97138        else: 
    98             doomed = [k for (i, k) in enumerate(filelist) if i % self._cull_frequency == 0] 
    99         for fname in doomed: 
     139            doomed = [os.path.join(self._dir, k) for (i, k) in enumerate(filelist) if i % self._cull_frequency == 0] 
     140 
     141        for topdir in doomed: 
    100142            try: 
    101                 os.remove(os.path.join(self._dir, fname)) 
     143                for root, _, files in os.walk(topdir): 
     144                    for file in files: 
     145                        self._delete(os.path.join(root,file)) 
    102146            except (IOError, OSError): 
    103147                pass 
    104  
     148             
    105149    def _createdir(self): 
    106150        try: 
    107151            os.makedirs(self._dir) 
     
    109153            raise EnvironmentError, "Cache directory '%s' does not exist and could not be created'" % self._dir 
    110154 
    111155    def _key_to_file(self, key): 
    112         return os.path.join(self._dir, urlquote_plus(key)) 
     156        path = md5.new(key.encode('utf-8')).hexdigest() 
     157        path = os.path.join(path[:2], path[2:4], path[4:]) 
     158        return os.path.join(self._dir, path) 
  • a/tests/regressiontests/cache/tests.py

    old new  
    2424 
    2525    def test_add(self): 
    2626        # test add (only add if key isn't already in cache) 
    27         cache.add("addkey1", "value") 
     27        cache.set("addkey1", "value") 
    2828        cache.add("addkey1", "newvalue") 
    2929        self.assertEqual(cache.get("addkey1"), "value") 
    30  
     30         
    3131    def test_non_existent(self): 
    3232        # get with non-existent keys 
    3333        self.assertEqual(cache.get("does_not_exist"), None) 
     
    7777 
    7878    def test_expiration(self): 
    7979        # expiration 
    80         cache.set('expire', 'very quickly', 1) 
     80        cache.set('expire1', 'very quickly', 1) 
     81        cache.set('expire2', 'very quickly', 1) 
     82        cache.set('expire3', 'very quickly', 1) 
    8183        time.sleep(2) 
    82         self.assertEqual(cache.get("expire"), None) 
     84         
     85        self.assertEqual(cache.get("expire1"), None) 
     86         
     87        cache.add("expire2", "newvalue") 
     88        self.assertEqual(cache.get("expire2"), "newvalue") 
     89     
     90        self.assertEqual(cache.has_key("expire3"), False) 
    8391 
    8492    def test_unicode(self): 
    8593        stuff = {