cmlenz@4: # -*- coding: utf-8 -*- cmlenz@4: # cmlenz@66: # Copyright (C) 2006 Edgewall Software cmlenz@21: # Copyright (C) 2006 Matthew Good cmlenz@4: # All rights reserved. cmlenz@4: # cmlenz@4: # This software is licensed as described in the file COPYING, which cmlenz@4: # you should have received as part of this distribution. The terms cmlenz@66: # are also available at http://markup.edgewall.org/wiki/License. cmlenz@4: # cmlenz@4: # This software consists of voluntary contributions made by many cmlenz@4: # individuals. For the exact contribution history, see the revision cmlenz@66: # history and logs, available at http://markup.edgewall.org/log/. cmlenz@4: cmlenz@29: """Basic support for the template engine plugin API used by TurboGears and cmlenz@29: CherryPy/Buffet. cmlenz@29: """ cmlenz@29: cmlenz@4: from pkg_resources import resource_filename cmlenz@4: cmlenz@182: from markup.core import Attrs, Stream, QName cmlenz@192: from markup.eval import Undefined cmlenz@192: from markup.input import HTML, XML cmlenz@4: from markup.template import Context, Template, TemplateLoader cmlenz@4: cmlenz@192: def ET(element): cmlenz@161: """Converts the given ElementTree element to a markup stream.""" mgood@72: tag_name = element.tag mgood@72: if tag_name.startswith('{'): mgood@72: tag_name = tag_name[1:] mgood@72: tag_name = QName(tag_name) cmlenz@183: attrib = Attrs(element.items()) mgood@72: cmlenz@161: yield (Stream.START, (tag_name, attrib), (None, -1, -1)) mgood@72: if element.text: cmlenz@151: yield Stream.TEXT, element.text, (None, -1, -1) mgood@72: for child in element.getchildren(): cmlenz@161: for item in et_to_stream(child): mgood@72: yield item cmlenz@151: yield Stream.END, tag_name, (None, -1, -1) mgood@72: if element.tail: cmlenz@151: yield Stream.TEXT, element.tail, (None, -1, -1) cmlenz@4: cmlenz@77: cmlenz@4: class TemplateEnginePlugin(object): cmlenz@29: """Implementation of the plugin API.""" cmlenz@4: cmlenz@4: def __init__(self, extra_vars_func=None, options=None): cmlenz@4: if options is None: cmlenz@4: options = {} cmlenz@4: # TODO get loader_args from the options dict cmlenz@4: cmlenz@4: self.loader = TemplateLoader(auto_reload=True) cmlenz@4: self.options = options cmlenz@4: self.get_extra_vars = extra_vars_func cmlenz@4: cmlenz@4: def load_template(self, templatename): cmlenz@4: """Find a template specified in python 'dot' notation.""" cmlenz@4: divider = templatename.rfind('.') cmlenz@4: if divider >= 0: cmlenz@4: package = templatename[:divider] cmlenz@4: basename = templatename[divider + 1:] + '.html' cmlenz@21: templatename = resource_filename(package, basename) cmlenz@4: cmlenz@4: return self.loader.load(templatename) cmlenz@4: cmlenz@4: def render(self, info, format='html', fragment=False, template=None): cmlenz@21: """Render the template to a string using the provided info.""" cmlenz@4: return self.transform(info, template).render(method=format) cmlenz@4: cmlenz@4: def transform(self, info, template): cmlenz@21: """Render the output to an event stream.""" cmlenz@4: if not isinstance(template, Template): cmlenz@4: template = self.load_template(template) cmlenz@4: cmlenz@192: data = {'ET': ET, 'HTML': HTML, 'XML': XML} cmlenz@4: if self.get_extra_vars: cmlenz@4: data.update(self.get_extra_vars()) cmlenz@4: data.update(info) cmlenz@192: ctxt = Context(**data) cmlenz@4: cmlenz@192: # Some functions for Kid compatibility cmlenz@192: def defined(name): cmlenz@192: return ctxt.get(name, Undefined) is not Undefined cmlenz@192: ctxt['defined'] = defined cmlenz@192: def value_of(name, default=None): cmlenz@192: val = ctxt.get(name, Undefined) cmlenz@192: if val is not Undefined: cmlenz@192: return val cmlenz@192: return default cmlenz@192: ctxt['value_of'] = value_of cmlenz@192: cmlenz@192: return template.generate(ctxt)