view genshi/template/plugin.py @ 417:359451249ce0 trunk

filter empty values from the plugin search path
author mgood
date Mon, 12 Mar 2007 21:35:18 +0000
parents afd4a51442fa
children 073640758a42
line wrap: on
line source
# -*- coding: utf-8 -*-
#
# Copyright (C) 2006-2007 Edgewall Software
# Copyright (C) 2006 Matthew Good
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://genshi.edgewall.org/wiki/License.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://genshi.edgewall.org/log/.

"""Basic support for the template engine plugin API used by TurboGears and
CherryPy/Buffet.
"""

from pkg_resources import resource_filename

from genshi.input import ET, HTML, XML
from genshi.output import DocType
from genshi.template.base import Context, Template
from genshi.template.loader import TemplateLoader
from genshi.template.markup import MarkupTemplate
from genshi.template.text import TextTemplate

__all__ = ['ConfigurationError', 'MarkupTemplateEnginePlugin',
           'TextTemplateEnginePlugin']


class ConfigurationError(Exception):
    """Exception raised when invalid plugin options are encountered."""


class AbstractTemplateEnginePlugin(object):
    """Implementation of the plugin API."""

    template_class = None
    extension = None

    def __init__(self, extra_vars_func=None, options=None):
        self.get_extra_vars = extra_vars_func
        if options is None:
            options = {}
        self.options = options

        self.default_encoding = options.get('genshi.default_encoding', 'utf-8')
        auto_reload = options.get('genshi.auto_reload', '1')
        if isinstance(auto_reload, basestring):
            auto_reload = auto_reload.lower() in ('1', 'on', 'yes', 'true')
        search_path = filter(None, options.get('genshi.search_path', '').split(':'))
        self.use_package_naming = not search_path
        try:
            max_cache_size = int(options.get('genshi.max_cache_size', 25))
        except ValueError:
            raise ConfigurationError('Invalid value for max_cache_size: "%s"' %
                                     options.get('genshi.max_cache_size'))

        self.loader = TemplateLoader(filter(None, search_path),
                                     auto_reload=auto_reload,
                                     max_cache_size=max_cache_size,
                                     default_class=self.template_class)

    def load_template(self, templatename, template_string=None):
        """Find a template specified in python 'dot' notation, or load one from
        a string.
        """
        if template_string is not None:
            return self.template_class(template_string)

        if self.use_package_naming:
            divider = templatename.rfind('.')
            if divider >= 0:
                package = templatename[:divider]
                basename = templatename[divider + 1:] + self.extension
                templatename = resource_filename(package, basename)

        return self.loader.load(templatename)

    def _get_render_options(self, format=None):
        if format is None:
            format = self.default_format
        kwargs = {'method': format}
        if self.default_encoding:
            kwargs['encoding'] = self.default_encoding
        return kwargs

    def render(self, info, format=None, fragment=False, template=None):
        """Render the template to a string using the provided info."""
        kwargs = self._get_render_options(format=format)
        return self.transform(info, template).render(**kwargs)

    def transform(self, info, template):
        """Render the output to an event stream."""
        if not isinstance(template, Template):
            template = self.load_template(template)
        ctxt = Context(**info)

        # Some functions for Kid compatibility
        def defined(name):
            return name in ctxt
        ctxt['defined'] = defined
        def value_of(name, default=None):
            return ctxt.get(name, default)
        ctxt['value_of'] = value_of

        return template.generate(ctxt)


class MarkupTemplateEnginePlugin(AbstractTemplateEnginePlugin):
    """Implementation of the plugin API for markup templates."""

    template_class = MarkupTemplate
    extension = '.html'

    doctypes = {'html': DocType.HTML, 'html-strict': DocType.HTML_STRICT,
                'html-transitional': DocType.HTML_TRANSITIONAL,
                'xhtml': DocType.XHTML, 'xhtml-strict': DocType.XHTML_STRICT,
                'xhtml-transitional': DocType.XHTML_TRANSITIONAL}

    def __init__(self, extra_vars_func=None, options=None):
        AbstractTemplateEnginePlugin.__init__(self, extra_vars_func, options)

        doctype = self.options.get('genshi.default_doctype')
        if doctype and doctype not in self.doctypes:
            raise ConfigurationError('Unknown doctype "%s"' % doctype)
        self.default_doctype = self.doctypes.get(doctype)

        format = self.options.get('genshi.default_format', 'html')
        if format not in ('html', 'xhtml', 'xml', 'text'):
            raise ConfigurationError('Unknown output format "%s"' % format)
        self.default_format = format

    def _get_render_options(self, format=None):
        kwargs = super(MarkupTemplateEnginePlugin,
                       self)._get_render_options(format)
        if self.default_doctype:
            kwargs['doctype'] = self.default_doctype
        return kwargs

    def transform(self, info, template):
        """Render the output to an event stream."""
        data = {'ET': ET, 'HTML': HTML, 'XML': XML}
        if self.get_extra_vars:
            data.update(self.get_extra_vars())
        data.update(info)
        return super(MarkupTemplateEnginePlugin, self).transform(data, template)


class TextTemplateEnginePlugin(AbstractTemplateEnginePlugin):
    """Implementation of the plugin API for text templates."""

    template_class = TextTemplate
    extension = '.txt'
    default_format = 'text'
Copyright (C) 2012-2017 Edgewall Software