Selaa lähdekoodia

Finished first complete version of Cache object, tested only in live environment.

Taddeus Kroes 14 vuotta sitten
vanhempi
sitoutus
bd10e62e94
1 muutettua tiedostoa jossa 71 lisäystä ja 24 poistoa
  1. 71 24
      cache.py

+ 71 - 24
cache.py

@@ -1,9 +1,11 @@
 import web
+import cPickle as pickle
 from time import gmtime
 from datetime import datetime, timedelta
 from os import mkdir
 from os.path import getmtime, exists
-from cPickle import load, dump
+from hashlib import md5
+from copy import copy
 
 
 ADMINISTRATION_FILE = 'administration.py'
@@ -29,7 +31,7 @@ def assert_file_exists(path):
 
 
 def seconds_to_datetime(seconds):
-    return datetime(gmtime(seconds)[:7])
+    return datetime(*gmtime(seconds)[:7])
 
 
 class Cache:
@@ -59,6 +61,10 @@ class Cache:
     def assert_files_exist(self):
         map(assert_file_exists, self.files)
 
+    def __str__(self):
+        return '<Cache filename=%s files=[%s]>' \
+               % (self.filename(), ','.join(self.files))
+
     def add(self, path, absolute=False):
         """
         Add a file to the cache object. Requires the full path to the file,
@@ -101,18 +107,35 @@ class Cache:
         if not exists(self.cached):
             mkdir(self.cached)
 
-    def save_administration(self):
+    def save_administration(self, old_admin={}):
         """
         Generate a Python file containing the modification dates of the cached
-        file list.
+        file list. The old_admin paramter can contain additional files which
+        are not in this cache object, but do need to be kept in the
+        administration for other cache objects.
         """
         self.assert_modification_dates_exist()
         self.assert_cached_folder_exist()
 
+        admin = copy(old_admin)
+        admin.update(self.modified)
+
         f = open(self.cached + ADMINISTRATION_FILE, 'w')
-        dump(self.modified, f)
+        pickle.dump(self.modified, f)
+        f.close()
+
+    def load_administration(self):
+        path = self.cached + ADMINISTRATION_FILE
+
+        if not exists(path):
+            return {}
+
+        f = open(path, 'r')
+        modified = pickle.load(f)
         f.close()
 
+        return modified
+
     def last_modified(self):
         self.assert_modification_dates_exist()
 
@@ -123,10 +146,16 @@ class Cache:
         Generate an Etag for the cache object, using the names of the files
         included and the latest modification date.
         """
-        return self.filename() + str(self.last_modified())
+        h = md5()
+        h.update(','.join(self.files) + str(self.last_modified()))
+
+        return h.hexdigest()
 
     def filename(self):
-        return ','.join(self.files)
+        h = md5()
+        h.update(','.join(self.files))
+
+        return self.cached + h.hexdigest()
 
     def concatenate(self):
         contents = ''
@@ -143,27 +172,45 @@ class Cache:
         # Update cached file
         last_modified = self.last_modified()
         path = self.filename()
-        server_modified = not exists(path) or getmtime(path) < last_modified
+        admin = self.load_administration()
+
+        if not exists(path):
+            web.debug('Cached file "%s" does not exist yet, generating it...')
+            server_modified = True
+        else:
+            server_modified = False
+
+            for f_path, f_modified in self.modified.iteritems():
+                if f_path not in admin:
+                    web.debug('File "%s" has been added.' % f_path)
+                    server_modified = True
+                elif f_modified > admin[f_path]:
+                    web.debug('File "%s" has been updated.' % f_path)
+                    server_modified = True
 
         if server_modified:
-            content = self.concatenate()
+            self.save_administration(admin)
 
+            content = self.concatenate()
             f = open(path, 'w')
             f.write(content)
             f.close()
 
-        #try:
-        web.http.modified(seconds_to_datetime(last_modified), self.etag())
-        web.http.expires(timedelta(**self.expires))
-        web.header('Cache-Control', 'private')
-
-        if not server_modified:
-            # Concatenated content has not been loaded yet, read the cached
-            # file
-            f = open(path, 'r')
-            content = f.read()
-            f.close()
-
-        return content
-        #except web.NotModified as e:
-        #    web.header('Status', e.message)
+        try:
+            web.http.modified(seconds_to_datetime(last_modified), self.etag())
+            web.http.expires(timedelta(**self.expires))
+            web.header('Cache-Control', 'private')
+
+            if not server_modified:
+                # Concatenated content has not been loaded yet, read the cached
+                # file
+                web.debug('Cached file "%s" already exists, sending content...')
+                f = open(path, 'r')
+                content = f.read()
+                f.close()
+
+            return content
+        except web.NotModified as e:
+            web.debug('Cached file "%s" not modified, setting 304 header...' \
+                      % path)
+            raise e