Edgewall Software

source: branches/stable/0.4.x/genshi/template/plugin.py

Last change on this file was 647, checked in by cmlenz, 16 years ago

Ported [646] to 0.4.x branch.

  • Property svn:eol-style set to native
File size: 6.1 KB
Line 
1# -*- coding: utf-8 -*-
2#
3# Copyright (C) 2006-2007 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
16CherryPy/Buffet.
17"""
18
19from pkg_resources import resource_filename
20
21from genshi.input import ET, HTML, XML
22from genshi.output import DocType
23from genshi.template.base import Template
24from genshi.template.loader import TemplateLoader
25from genshi.template.markup import MarkupTemplate
26from genshi.template.text import TextTemplate
27
28__all__ = ['ConfigurationError', 'AbstractTemplateEnginePlugin',
29           'MarkupTemplateEnginePlugin', 'TextTemplateEnginePlugin']
30__docformat__ = 'restructuredtext en'
31
32
33class ConfigurationError(ValueError):
34    """Exception raised when invalid plugin options are encountered."""
35
36
37class AbstractTemplateEnginePlugin(object):
38    """Implementation of the plugin API."""
39
40    template_class = None
41    extension = None
42
43    def __init__(self, extra_vars_func=None, options=None):
44        self.get_extra_vars = extra_vars_func
45        if options is None:
46            options = {}
47        self.options = options
48
49        self.default_encoding = options.get('genshi.default_encoding', 'utf-8')
50        auto_reload = options.get('genshi.auto_reload', '1')
51        if isinstance(auto_reload, basestring):
52            auto_reload = auto_reload.lower() in ('1', 'on', 'yes', 'true')
53        search_path = filter(None, options.get('genshi.search_path', '').split(':'))
54        self.use_package_naming = not search_path
55        try:
56            max_cache_size = int(options.get('genshi.max_cache_size', 25))
57        except ValueError:
58            raise ConfigurationError('Invalid value for max_cache_size: "%s"' %
59                                     options.get('genshi.max_cache_size'))
60
61        loader_callback = options.get('genshi.loader_callback', None)
62        if loader_callback and not callable(loader_callback):
63            raise ConfigurationError('loader callback must be a function')
64
65        lookup_errors = options.get('genshi.lookup_errors', 'lenient')
66        if lookup_errors not in ('lenient', 'strict'):
67            raise ConfigurationError('Unknown lookup errors mode "%s"' %
68                                     lookup_errors)
69
70        self.loader = TemplateLoader(filter(None, search_path),
71                                     auto_reload=auto_reload,
72                                     max_cache_size=max_cache_size,
73                                     default_class=self.template_class,
74                                     variable_lookup=lookup_errors,
75                                     callback=loader_callback)
76
77    def load_template(self, templatename, template_string=None):
78        """Find a template specified in python 'dot' notation, or load one from
79        a string.
80        """
81        if template_string is not None:
82            return self.template_class(template_string)
83
84        if self.use_package_naming:
85            divider = templatename.rfind('.')
86            if divider >= 0:
87                package = templatename[:divider]
88                basename = templatename[divider + 1:] + self.extension
89                templatename = resource_filename(package, basename)
90
91        return self.loader.load(templatename)
92
93    def _get_render_options(self, format=None):
94        if format is None:
95            format = self.default_format
96        kwargs = {'method': format}
97        if self.default_encoding:
98            kwargs['encoding'] = self.default_encoding
99        return kwargs
100
101    def render(self, info, format=None, fragment=False, template=None):
102        """Render the template to a string using the provided info."""
103        kwargs = self._get_render_options(format=format)
104        return self.transform(info, template).render(**kwargs)
105
106    def transform(self, info, template):
107        """Render the output to an event stream."""
108        if not isinstance(template, Template):
109            template = self.load_template(template)
110        return template.generate(**info)
111
112
113class MarkupTemplateEnginePlugin(AbstractTemplateEnginePlugin):
114    """Implementation of the plugin API for markup templates."""
115
116    template_class = MarkupTemplate
117    extension = '.html'
118
119    def __init__(self, extra_vars_func=None, options=None):
120        AbstractTemplateEnginePlugin.__init__(self, extra_vars_func, options)
121
122        default_doctype = self.options.get('genshi.default_doctype')
123        if default_doctype:
124            doctype = DocType.get(default_doctype)
125            if doctype is None:
126                raise ConfigurationError('Unknown doctype %r' % default_doctype)
127            self.default_doctype = doctype
128        else:
129            self.default_doctype = None
130
131        format = self.options.get('genshi.default_format', 'html').lower()
132        if format not in ('html', 'xhtml', 'xml', 'text'):
133            raise ConfigurationError('Unknown output format %r' % format)
134        self.default_format = format
135
136    def _get_render_options(self, format=None):
137        kwargs = super(MarkupTemplateEnginePlugin,
138                       self)._get_render_options(format)
139        if self.default_doctype:
140            kwargs['doctype'] = self.default_doctype
141        return kwargs
142
143    def transform(self, info, template):
144        """Render the output to an event stream."""
145        data = {'ET': ET, 'HTML': HTML, 'XML': XML}
146        if self.get_extra_vars:
147            data.update(self.get_extra_vars())
148        data.update(info)
149        return super(MarkupTemplateEnginePlugin, self).transform(data, template)
150
151
152class TextTemplateEnginePlugin(AbstractTemplateEnginePlugin):
153    """Implementation of the plugin API for text templates."""
154
155    template_class = TextTemplate
156    extension = '.txt'
157    default_format = 'text'
Note: See TracBrowser for help on using the repository browser.