普通文本  |  137行  |  4.25 KB

# -*- coding: utf-8 -*-
"""
    webapp2_extras.mako
    ===================

    Mako template support for webapp2.

    Learn more about Mako: http://www.makotemplates.org/

    :copyright: 2011 by tipfy.org.
    :license: Apache Sotware License, see LICENSE for details.
"""
from __future__ import absolute_import

from mako import lookup

import webapp2

#: Default configuration values for this module. Keys are:
#:
#: template_path
#:     Directory for templates. Default is `templates`.
default_config = {
    'template_path': 'templates',
}


class Mako(object):
    """Wrapper for configurable and cached Mako environment.

    To used it, set it as a cached property in a base `RequestHandler`::

        import webapp2

        from webapp2_extras import mako

        class BaseHandler(webapp2.RequestHandler):

            @webapp2.cached_property
            def mako(self):
                # Returns a Mako renderer cached in the app registry.
                return mako.get_mako(app=self.app)

            def render_response(self, _template, **context):
                # Renders a template and writes the result to the response.
                rv = self.mako.render_template(_template, **context)
                self.response.write(rv)

    Then extended handlers can render templates directly::

        class MyHandler(BaseHandler):
            def get(self):
                context = {'message': 'Hello, world!'}
                self.render_response('my_template.html', **context)
    """

    #: Configuration key.
    config_key = __name__

    #: Loaded configuration.
    config = None

    def __init__(self, app, config=None):
        self.config = config = app.config.load_config(self.config_key,
            default_values=default_config, user_values=config,
            required_keys=None)

        directories = config.get('template_path')
        if isinstance(directories, basestring):
            directories = [directories]

        self.environment = lookup.TemplateLookup(directories=directories,
                                                 output_encoding='utf-8',
                                                 encoding_errors='replace')

    def render_template(self, _filename, **context):
        """Renders a template and returns a response object.

        :param _filename:
            The template filename, related to the templates directory.
        :param context:
            Keyword arguments used as variables in the rendered template.
            These will override values set in the request context.
        :returns:
            A rendered template.
        """
        template = self.environment.get_template(_filename)
        return template.render_unicode(**context)


# Factories -------------------------------------------------------------------


#: Key used to store :class:`Mako` in the app registry.
_registry_key = 'webapp2_extras.mako.Mako'


def get_mako(factory=Mako, key=_registry_key, app=None):
    """Returns an instance of :class:`Mako` from the app registry.

    It'll try to get it from the current app registry, and if it is not
    registered it'll be instantiated and registered. A second call to this
    function will return the same instance.

    :param factory:
        The callable used to build and register the instance if it is not yet
        registered. The default is the class :class:`Mako` itself.
    :param key:
        The key used to store the instance in the registry. A default is used
        if it is not set.
    :param app:
        A :class:`webapp2.WSGIApplication` instance used to store the instance.
        The active app is used if it is not set.
    """
    app = app or webapp2.get_app()
    mako = app.registry.get(key)
    if not mako:
        mako = app.registry[key] = factory(app)

    return mako


def set_mako(mako, key=_registry_key, app=None):
    """Sets an instance of :class:`Mako` in the app registry.

    :param store:
        An instance of :class:`Mako`.
    :param key:
        The key used to retrieve the instance from the registry. A default
        is used if it is not set.
    :param request:
        A :class:`webapp2.WSGIApplication` instance used to retrieve the
        instance. The active app is used if it is not set.
    """
    app = app or webapp2.get_app()
    app.registry[key] = mako