"""Servlet factory for Kid templates.
This allows you to run kid template files directly from within Webware.
Compiled templates are cached either along with the templates or in the
Cache/KidKit subdirectory of the WebKit working directory.
Note that the Kid package itself is not part of Webware; you need to install
it separately. You also need to install the ElementTree package to use Kid.
CREDITS:
* Kid has been developed by Ryan Tomayko (http://lesscode.org).
* ElementTree has been developed by Fredrik Lundh (http://effbot.org).
* KidKit was contributed by Winston Wolff (winstonw<at>stratolab.com).
Based on the Cheetah servlet factory. No caching, fixed servlet hook.
* Improved version contributed by Christoph Zwerschke (cito<at>online.de).
Based on the PSP servlet factory. Supports caching and servlet hooks.
"""
import os, sys, time
from glob import glob
from WebKit.ServletFactory import ServletFactory
from WebKit.Servlet import Servlet
from WebKit.Page import Page
defaultHook = Page.respond
defaultOutput = 'html'
from kid import load_template, output_methods
from kid.compiler import KidFile
from KidKit.Examples.KidExamplePage import KidExamplePage
def kidClass(module):
"""Return a WebKit servlet class for a Kid template module."""
try:
hook = module.hook
except AttributeError:
hook = defaultHook
assert type(hook) == type(defaultHook)
ServletClass = hook.im_class
assert issubclass(ServletClass, Servlet)
writeMethod = hook.__name__
try:
output = module.output
except:
output = defaultOutput
assert output in output_methods
class KidServlet(ServletClass):
"""The base class for a Kid servlet."""
_module = module
_template = module.Template()
_output = output
def writeTemplate(self, *args, **kwargs):
self._template.servlet = self
response = self._response
fragment = response.size() > 0
self._template.write(self._response,
fragment=fragment, output=output)
setattr(KidServlet, writeMethod, KidServlet.writeTemplate)
return KidServlet
class KidServletFactory(ServletFactory):
"""Servlet factory for Kid templates."""
def __init__(self, application):
ServletFactory.__init__(self, application)
setting = application.setting
global defaultOutput
defaultOutput = setting('KidOutputMethod', defaultOutput)
self._cacheTemplates = setting('CacheKidTemplates', True)
self._useCache = setting('UseKidKitCache', False)
if self._useCache:
self._cacheSource = setting('CacheKidSource', True)
self._clearCache = setting('ClearKidCacheOnStart', False)
self._cacheDir = application.serverSidePath('Cache/KidKit')
if self._clearCache:
self.clearFileCache()
else:
self._cacheSource = self._clearCache = False
self._cacheDir = None
t = ['_'] * 256
from string import digits, letters
for c in digits + letters:
t[ord(c)] = c
self._classNameTrans = ''.join(t)
def uniqueness(self):
return 'file'
def extensions(self):
return ['.kid']
def flushCache(self):
"""Clean out the cache of classes in memory and on disk."""
ServletFactory.flushCache(self)
if self._clearCache:
self.clearFileCache()
def clearFileCache(self):
"""Clear class files stored on disk."""
files = glob(os.path.join(self._cacheDir, '*.*'))
map(os.remove, files)
def computeClassName(self, pagename):
"""Generates a (hopefully) unique class/file name for each Kid file.
Argument: pagename: the path to the Kid template file
Returns: a unique name for the class generated fom this Kid file
"""
return os.path.splitdrive(pagename)[1].translate(self._classNameTrans)
def loadClass(self, transaction, path):
"""Load servlet class for the given Kid template."""
classname = self.computeClassName(path)
if self._cacheTemplates and self._useCache:
mtime = os.path.getmtime(path)
classfile = os.path.join(self._cacheDir, classname + ".py")
if not self._cacheSource:
classfile += __debug__ and 'c' or 'o'
if not os.path.exists(classfile) \
or os.path.getmtime(classfile) != mtime:
kidFile = KidFile(path)
if self._cacheSource:
kidFile.dump_source(classfile)
else:
kidFile.dump_code(classfile)
os.utime(classfile, (os.path.getatime(classfile), mtime))
module = self.importAsPackage(transaction, classfile)
else:
module = load_template(path, cache=self._cacheTemplates)
module.__orig_file__ = path
self._imp.watchFile(path)
theClass = kidClass(module)
theClass._orig_file = path
theClass.__name__ = self.computeClassName(path)
return theClass