[Checkins] SVN: z3c.pt/trunk/ Added persistent file cache functionality. If the environment variable is set, each file system based template will add a directory to the cache (currently a SHA-1 of the file's absolute path is used as the folder name) and in the folder one file per params for the template (cache filename is the hash of the params). Once a template file is initialized, an instance local registry is added, which then looks up all cached files and pre-populates the registry with the render functions.

Hanno Schlichting plone at hannosch.info
Sun Jul 6 10:41:28 EDT 2008


Log message for revision 88069:
  Added persistent file cache functionality. If the environment variable is set, each file system based template will add a directory to the cache (currently a SHA-1 of the file's absolute path is used as the folder name) and in the folder one file per params for the template (cache filename is the hash of the params). Once a template file is initialized, an instance local registry is added, which then looks up all cached files and pre-populates the registry with the render functions.
  

Changed:
  U   z3c.pt/trunk/docs/HISTORY.txt
  U   z3c.pt/trunk/src/z3c/pt/codegen.py
  U   z3c.pt/trunk/src/z3c/pt/filecache.py
  U   z3c.pt/trunk/src/z3c/pt/template.py

-=-
Modified: z3c.pt/trunk/docs/HISTORY.txt
===================================================================
--- z3c.pt/trunk/docs/HISTORY.txt	2008-07-06 10:30:53 UTC (rev 88068)
+++ z3c.pt/trunk/docs/HISTORY.txt	2008-07-06 14:41:26 UTC (rev 88069)
@@ -4,6 +4,14 @@
 Version 0.8.x
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
+- Added persistent file cache functionality. If the environment variable is
+  set, each file system based template will add a directory to the cache
+  (currently a SHA-1 of the file's absolute path is used as the folder name)
+  and in the folder one file per params for the template (cache filename is
+  the hash of the params). Once a template file is initialized, an instance
+  local registry is added, which then looks up all cached files and
+  pre-populates the registry with the render functions.
+
 - Fixed interpolation edge case bugs.
   [malthe]
 

Modified: z3c.pt/trunk/src/z3c/pt/codegen.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/codegen.py	2008-07-06 10:30:53 UTC (rev 88068)
+++ z3c.pt/trunk/src/z3c/pt/codegen.py	2008-07-06 14:41:26 UTC (rev 88069)
@@ -78,4 +78,4 @@
         return hash(self.code)
 
     def __repr__(self):
-        return '%s(%r)' % (self.__class__.__name__, self.source)
+        return '%s(%r)' % (self.__class__.__name__, self.code)

Modified: z3c.pt/trunk/src/z3c/pt/filecache.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/filecache.py	2008-07-06 10:30:53 UTC (rev 88068)
+++ z3c.pt/trunk/src/z3c/pt/filecache.py	2008-07-06 14:41:26 UTC (rev 88069)
@@ -4,12 +4,16 @@
 import struct
 
 from imp import get_magic
+from sha import sha
+from UserDict import UserDict
 
+from z3c.pt.config import FILECACHE
+
 MAGIC = get_magic()
 
-def code_read(self, filename, timestamp):
+def code_read(filename, timestamp):
     code = None
-    if os.path.isfile(self.code_filename):
+    if os.path.isfile(filename):
         fd = open(filename, 'rb')
         if fd.read(4) == MAGIC:
             ctimestamp = struct.unpack('<I', fd.read(4))[0]
@@ -18,11 +22,10 @@
         fd.close()
     return code
 
-def code_write(self, source, filename, timestamp):
+def code_write(code, filename, timestamp):
     try:
         fd = open(filename, 'wb')
         try:
-            code = compile(source, filename, "exec")
             # Create a byte code file. See the py_compile module
             fd.write('\0\0\0\0')
             fd.write(struct.pack("<I", timestamp))
@@ -35,3 +38,33 @@
         py_compile.set_creator_type(filename)
     except (IOError, OSError):
         pass
+
+class CachingDict(UserDict):
+
+    def __init__(self, filename, mtime, pagetemplate):
+        UserDict.__init__(self)
+
+        if FILECACHE:
+            # Update ourselves with the values from the cache file
+            filename = sha(filename).hexdigest()
+            self.cachedir = os.path.join(FILECACHE, filename)
+            self.mtime = mtime
+
+            if os.path.isdir(self.cachedir):
+                for cachefile in os.listdir(self.cachedir):
+                    value = None
+                    cachepath = os.path.join(self.cachedir, cachefile)
+                    code = code_read(cachepath, self.mtime)
+                    if code is not None:
+                        self[int(cachefile)] = pagetemplate.execute(code)
+                        pagetemplate._v_last_read = mtime
+
+    # If we don't have a file cache, behave like a normal instance level dict
+    if FILECACHE:
+        def store(self, params, code):
+            key = hash(''.join(params))
+            if not os.path.isdir(self.cachedir):
+                os.mkdir(self.cachedir)
+
+            cachefile = os.path.join(self.cachedir, str(key))
+            code_write(code, cachefile, self.mtime)

Modified: z3c.pt/trunk/src/z3c/pt/template.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/template.py	2008-07-06 10:30:53 UTC (rev 88068)
+++ z3c.pt/trunk/src/z3c/pt/template.py	2008-07-06 14:41:26 UTC (rev 88069)
@@ -3,12 +3,15 @@
 import codegen
 import traceback
 
-from z3c.pt.config import DEBUG_MODE, PROD_MODE
+from z3c.pt.config import DEBUG_MODE, PROD_MODE, FILECACHE
+from z3c.pt import filecache
+import z3c.pt.generation
 
 class BaseTemplate(object):
+
     registry = {}
     default_expression = 'python'
-    
+
     def __init__(self, body, default_expression=None):
         self.body = body
         self.signature = hash(body)
@@ -93,6 +96,7 @@
         return u"<%s %d>" % (self.__class__.__name__, id(self))
 
 class BaseTemplateFile(BaseTemplate):
+
     def __init__(self, filename):
         BaseTemplate.__init__(self, None)
 
@@ -110,6 +114,7 @@
         # make sure file exists
         os.lstat(filename)
         self.filename = filename
+        self.registry = filecache.CachingDict(filename, self.mtime(), self)
 
     def _get_filename(self):
         return getattr(self, '_filename', None)
@@ -130,6 +135,34 @@
             fs.write(self.source)
             fs.close()
 
+    def cook(self, params):
+        generator = self.translate(
+            self.body, params=params, default_expression=self.default_expression)
+        
+        source, _globals = generator()
+        
+        suite = codegen.Suite(source)
+
+        self.source = source
+        self.source_write()
+        self.annotations = generator.stream.annotations
+        
+        _globals.update(suite._globals)
+        if FILECACHE:
+            self.registry.store(params, suite.code)
+
+        return self.execute(suite.code, _globals)
+
+    def execute(self, code, _globals=None):
+        # TODO: This is evil. We need a better way to get all the globals
+        # independent from the generation step
+        if _globals is None:
+            _globals = codegen.Lookup.globals()
+            _globals['generation'] = z3c.pt.generation
+        _locals = {}
+        exec code in _globals, _locals
+        return _locals['render']
+
     def render(self, **kwargs):
         if self._cook_check():
             fd = open(self.filename, 'r')
@@ -138,8 +171,16 @@
             self.signature = hash(body)
             self._v_last_read = self.mtime()
 
-        return BaseTemplate.render(self, **kwargs)
+        signature = hash(''.join(kwargs))
+        template = self.registry.get(signature, None)
+        if template is None:
+            self.registry[signature] = template = self.cook(kwargs.keys())
 
+        if PROD_MODE:
+            return template(**kwargs)
+
+        return self.safe_render(template, **kwargs)
+
     def _cook_check(self):
         if self._v_last_read and PROD_MODE:
             return False



More information about the Checkins mailing list