[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