import sys import os import stat from django.http import HttpResponse from django.template import Context, RequestContext from os.path import join, isfile from django.conf import settings from django.template import TemplateDoesNotExist from django.template import defaultfilters from StringIO import StringIO from simpletal import simpleTAL, simpleTALES # this also prevents logging errors import logging import sys, traceback simpleTALLogger = logging.getLogger ("simpleTAL") simpleTALESLogger = logging.getLogger ("simpleTALES") simpleTALLogger.setLevel (logging.ERROR) simpleTALESLogger.setLevel (logging.ERROR) # django doesn't have any logging at all which # may keep the irc muppets happy, but supposing you # want to know what's going on, uncomment these lines #hdlr = logging.FileHandler('/tmp/simpletemplate.log') #simpleTALLogger.addHandler(hdlr) #simpleTALESLogger.addHandler(hdlr) _view_cache = {} _macro_cache = {} extension = ".pt" def get_template(template_name): """Returns a DjangoSimpleTAL object.""" # an optimisation that stop it looking for the file # if its been updated, before render it will get updated t = _view_cache.get(template_name) if t is None: error_msg = "Tried %s looking for the template %s and failed." % \ (str(settings.TEMPLATE_DIRS), template_name) raise ValueError, error_msg return t class DjangoSimpleTAL: def __init__(self, filename): self.filename = filename self.timestamp = 0 self._update() def _update(self): last = os.stat(self.filename)[stat.ST_MTIME] if last > self.timestamp: # if its been updated, reupdate it data = open(self.filename, "rb") self.template = simpleTAL.compileHTMLTemplate(data, inputEncoding = "utf-8") data.close() self.timestamp = last def __getattr__(self, attr): if hasattr(self.template, attr): return getattr(self.template, attr) else: raise AttributeError, attr def render(self, context, *args, **kw): self._update() # Copy all values from the Django context to the PT namespace ctx = createcontext() output = StringIO() # pull the django context in something of our liking try: # allow just a dictionary for k, v in context.items(): ctx.addGlobal(k, v) except AttributeError: # a django context object, is just a list of dicts for dct in context: for k, v in dct.items(): ctx.addGlobal(k, v) # pull the remaining kw in as well # args? not sure for k, v in kw.items(): ctx.addGlobal(k, v) self.template.expand(ctx, output, outputEncoding="utf-8") return output.getvalue() def createcontext(): ctx = simpleTALES.Context(allowPythonPath=1) # need to make this a little bit more efficient for item in _macro_cache.values(): item._update() ctx.addGlobal("filters", defaultfilters.register.filters) ctx.addGlobal("settings", {"MEDIA_URL":settings.MEDIA_URL,}) ctx.addGlobal("templates", _macro_cache) return ctx def findtemplates(): # load all the .pt into the _cache # so that we can refer to them in macros for dr in settings.TEMPLATE_DIRS: for root, dirs, files in os.walk(dr): for fle in files: if fle.endswith(extension): fle = os.path.join(root, fle) # a macro needs /foo/bar/recipes/temp.pt to be temp.pt macro_name = fle.split('/')[-1].split('.')[-2] # a view needs recipes/temp.pt view_name = fle.replace(dr, '') if view_name.startswith('/'): view_name = view_name[1:] # create it and reference in two places try: t = DjangoSimpleTAL(fle) except: traceback.print_exc(file=sys.stdout) raise ValueError, "* Warning failed to parse: %s" % fle # if it already exists, ignore: if _view_cache.has_key(view_name): print "* Warning, a template called %s exists, skipping %s" % \ (view_name, fle) continue _view_cache[view_name] = t _macro_cache[macro_name] = t # make sure we get all templates for macros findtemplates() def render_to_response(template_name, dictionary, context_instance=None): template = get_template(template_name) if context_instance is None: context_instance = Context(dictionary) return HttpResponse(template.render(context_instance))