Source code for webapp2_extras.jinja2

# -*- coding: utf-8 -*-
# Copyright 2011 webapp2 AUTHORS.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
webapp2_extras.jinja2
=====================

Jinja2 template support for webapp2.

Learn more about Jinja2: http://jinja.pocoo.org/
"""
import importlib

import webapp2

_jinja2 = importlib.import_module("jinja2")


#: Default configuration values for this module. Keys are:
#:
#: template_path
#:     Directory for templates. Default is `templates`.
#:
#: compiled_path
#:     Target for compiled templates. If set, uses the loader for compiled
#:     templates in production. If it ends with a '.zip' it will be treated
#:     as a zip file. Default is None.
#:
#: force_compiled
#:     Forces the use of compiled templates even in the development server.
#:
#: environment_args
#:     Keyword arguments used to instantiate the Jinja2 environment. By
#:     default autoescaping is enabled and two extensions are set:
#:     ``jinja2.ext.autoescape`` and ``jinja2.ext.with_``. For production it
#:     may be a good idea to set 'auto_reload' to False -- we don't need to
#:     check if templates changed after deployed.
#:
#: globals
#:     Extra global variables for the Jinja2 environment.
#:
#: filters
#:     Extra filters for the Jinja2 environment.
default_config = {
    'template_path': 'templates',
    'compiled_path': None,
    'force_compiled': False,
    'environment_args': {
        'autoescape': True,
        'extensions': [
            'jinja2.ext.autoescape',
            'jinja2.ext.with_',
        ],
    },
    'globals': None,
    'filters': None,
}


[docs]class Jinja2(object): """Wrapper for configurable and cached Jinja2 environment. To used it, set it as a cached property in a base `RequestHandler`:: import webapp2 from webapp2_extras import jinja2 class BaseHandler(webapp2.RequestHandler): @webapp2.cached_property def jinja2(self): # Returns a Jinja2 renderer cached in the app registry. return jinja2.get_jinja2(app=self.app) def render_response(self, _template, **context): # Renders a template and writes the result to the response. rv = self.jinja2.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
[docs] def __init__(self, app, config=None): """Initializes the Jinja2 object. :param app: A :class:`webapp2.WSGIApplication` instance. :param config: A dictionary of configuration values to be overridden. See the available keys in :data:`default_config`. """ self.config = config = app.config.load_config( self.config_key, default_values=default_config, user_values=config, required_keys=None ) kwargs = config['environment_args'].copy() enable_i18n = 'jinja2.ext.i18n' in kwargs.get('extensions', []) if 'loader' not in kwargs: template_path = config['template_path'] compiled_path = config['compiled_path'] use_compiled = not app.debug or config['force_compiled'] if compiled_path and use_compiled: # Use precompiled templates loaded from a module or zip. kwargs['loader'] = _jinja2.ModuleLoader(compiled_path) else: # Parse templates for every new environment instances. kwargs['loader'] = _jinja2.FileSystemLoader(template_path) # Initialize the environment. env = _jinja2.Environment(**kwargs) if config['globals']: env.globals.update(config['globals']) if config['filters']: env.filters.update(config['filters']) if enable_i18n: # Install i18n. from webapp2_extras import i18n env.install_gettext_callables( lambda x: i18n.gettext(x), lambda s, p, n: i18n.ngettext(s, p, n), newstyle=True) env.filters.update({ 'format_date': i18n.format_date, 'format_time': i18n.format_time, 'format_datetime': i18n.format_datetime, 'format_timedelta': i18n.format_timedelta, }) self.environment = env
[docs] 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. """ return self.environment.get_template(_filename).render(**context)
[docs] def get_template_attribute(self, filename, attribute): """Loads a macro (or variable) a template exports. This can be used to invoke a macro from within Python code. If you for example have a template named `_foo.html` with the following contents: .. sourcecode:: html+jinja {% macro hello(name) %}Hello {{ name }}!{% endmacro %} You can access this from Python code like this:: hello = get_template_attribute('_foo.html', 'hello') return hello('World') This function comes from `Flask`. :param filename: The template filename. :param attribute: The name of the variable of macro to acccess. """ template = self.environment.get_template(filename) return getattr(template.module, attribute)
# Factories ------------------------------------------------------------------- #: Key used to store :class:`Jinja2` in the app registry. _registry_key = 'webapp2_extras.jinja2.Jinja2'
[docs]def get_jinja2(factory=Jinja2, key=_registry_key, app=None): """Returns an instance of :class:`Jinja2` 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:`Jinja2` 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() jinja2 = app.registry.get(key) if not jinja2: jinja2 = app.registry[key] = factory(app) return jinja2
[docs]def set_jinja2(jinja2, key=_registry_key, app=None): """Sets an instance of :class:`Jinja2` in the app registry. :param store: An instance of :class:`Jinja2`. :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] = jinja2