| 1 | # -*- coding: utf-8 -*- |
|---|
| 2 | # |
|---|
| 3 | # Copyright (C) 2006 Edgewall Software |
|---|
| 4 | # Copyright (C) 2006 Matthew Good |
|---|
| 5 | # All rights reserved. |
|---|
| 6 | # |
|---|
| 7 | # This software is licensed as described in the file COPYING, which |
|---|
| 8 | # you should have received as part of this distribution. The terms |
|---|
| 9 | # are also available at http://genshi.edgewall.org/wiki/License. |
|---|
| 10 | # |
|---|
| 11 | # This software consists of voluntary contributions made by many |
|---|
| 12 | # individuals. For the exact contribution history, see the revision |
|---|
| 13 | # history and logs, available at http://genshi.edgewall.org/log/. |
|---|
| 14 | |
|---|
| 15 | """Basic support for the template engine plugin API used by TurboGears and |
|---|
| 16 | CherryPy/Buffet. |
|---|
| 17 | """ |
|---|
| 18 | |
|---|
| 19 | from pkg_resources import resource_filename |
|---|
| 20 | |
|---|
| 21 | from genshi.core import Attrs, Stream, QName |
|---|
| 22 | from genshi.eval import Undefined |
|---|
| 23 | from genshi.input import HTML, XML |
|---|
| 24 | from genshi.template import Context, MarkupTemplate, Template, TemplateLoader |
|---|
| 25 | |
|---|
| 26 | def ET(element): |
|---|
| 27 | """Converts the given ElementTree element to a markup stream.""" |
|---|
| 28 | tag_name = element.tag |
|---|
| 29 | if tag_name.startswith('{'): |
|---|
| 30 | tag_name = tag_name[1:] |
|---|
| 31 | tag_name = QName(tag_name) |
|---|
| 32 | attrib = Attrs(element.items()) |
|---|
| 33 | |
|---|
| 34 | yield (Stream.START, (tag_name, attrib), (None, -1, -1)) |
|---|
| 35 | if element.text: |
|---|
| 36 | yield Stream.TEXT, element.text, (None, -1, -1) |
|---|
| 37 | for child in element.getchildren(): |
|---|
| 38 | for item in ET(child): |
|---|
| 39 | yield item |
|---|
| 40 | yield Stream.END, tag_name, (None, -1, -1) |
|---|
| 41 | if element.tail: |
|---|
| 42 | yield Stream.TEXT, element.tail, (None, -1, -1) |
|---|
| 43 | |
|---|
| 44 | |
|---|
| 45 | class TemplateEnginePlugin(object): |
|---|
| 46 | """Implementation of the plugin API.""" |
|---|
| 47 | |
|---|
| 48 | def __init__(self, extra_vars_func=None, options=None): |
|---|
| 49 | if options is None: |
|---|
| 50 | options = {} |
|---|
| 51 | # TODO get loader_args from the options dict |
|---|
| 52 | |
|---|
| 53 | self.loader = TemplateLoader(auto_reload=True) |
|---|
| 54 | self.options = options |
|---|
| 55 | self.get_extra_vars = extra_vars_func |
|---|
| 56 | |
|---|
| 57 | def load_template(self, templatename, template_string=None): |
|---|
| 58 | """Find a template specified in python 'dot' notation, or load one from |
|---|
| 59 | a string. |
|---|
| 60 | """ |
|---|
| 61 | if template_string is not None: |
|---|
| 62 | return MarkupTemplate(template_string) |
|---|
| 63 | |
|---|
| 64 | divider = templatename.rfind('.') |
|---|
| 65 | if divider >= 0: |
|---|
| 66 | package = templatename[:divider] |
|---|
| 67 | basename = templatename[divider + 1:] + '.html' |
|---|
| 68 | templatename = resource_filename(package, basename) |
|---|
| 69 | |
|---|
| 70 | return self.loader.load(templatename) |
|---|
| 71 | |
|---|
| 72 | def render(self, info, format='html', fragment=False, template=None): |
|---|
| 73 | """Render the template to a string using the provided info.""" |
|---|
| 74 | return self.transform(info, template).render(method=format) |
|---|
| 75 | |
|---|
| 76 | def transform(self, info, template): |
|---|
| 77 | """Render the output to an event stream.""" |
|---|
| 78 | if not isinstance(template, Template): |
|---|
| 79 | template = self.load_template(template) |
|---|
| 80 | |
|---|
| 81 | data = {'ET': ET, 'HTML': HTML, 'XML': XML} |
|---|
| 82 | if self.get_extra_vars: |
|---|
| 83 | data.update(self.get_extra_vars()) |
|---|
| 84 | data.update(info) |
|---|
| 85 | ctxt = Context(**data) |
|---|
| 86 | |
|---|
| 87 | # Some functions for Kid compatibility |
|---|
| 88 | def defined(name): |
|---|
| 89 | return ctxt.get(name, Undefined) is not Undefined |
|---|
| 90 | ctxt['defined'] = defined |
|---|
| 91 | def value_of(name, default=None): |
|---|
| 92 | return ctxt.get(name, default) |
|---|
| 93 | ctxt['value_of'] = value_of |
|---|
| 94 | |
|---|
| 95 | return template.generate(ctxt) |
|---|