1# -*- coding: utf-8 -*- 2""" 3 webapp2_extras.jinja2 4 ===================== 5 6 Jinja2 template support for webapp2. 7 8 Learn more about Jinja2: http://jinja.pocoo.org/ 9 10 :copyright: 2011 by tipfy.org. 11 :license: Apache Sotware License, see LICENSE for details. 12""" 13from __future__ import absolute_import 14 15import jinja2 16 17import webapp2 18 19#: Default configuration values for this module. Keys are: 20#: 21#: template_path 22#: Directory for templates. Default is `templates`. 23#: 24#: compiled_path 25#: Target for compiled templates. If set, uses the loader for compiled 26#: templates in production. If it ends with a '.zip' it will be treated 27#: as a zip file. Default is None. 28#: 29#: force_compiled 30#: Forces the use of compiled templates even in the development server. 31#: 32#: environment_args 33#: Keyword arguments used to instantiate the Jinja2 environment. By 34#: default autoescaping is enabled and two extensions are set: 35#: ``jinja2.ext.autoescape`` and ``jinja2.ext.with_``. For production it 36#: may be a good idea to set 'auto_reload' to False -- we don't need to 37#: check if templates changed after deployed. 38#: 39#: globals 40#: Extra global variables for the Jinja2 environment. 41#: 42#: filters 43#: Extra filters for the Jinja2 environment. 44default_config = { 45 'template_path': 'templates', 46 'compiled_path': None, 47 'force_compiled': False, 48 'environment_args': { 49 'autoescape': True, 50 'extensions': [ 51 'jinja2.ext.autoescape', 52 'jinja2.ext.with_', 53 ], 54 }, 55 'globals': None, 56 'filters': None, 57} 58 59 60class Jinja2(object): 61 """Wrapper for configurable and cached Jinja2 environment. 62 63 To used it, set it as a cached property in a base `RequestHandler`:: 64 65 import webapp2 66 67 from webapp2_extras import jinja2 68 69 class BaseHandler(webapp2.RequestHandler): 70 71 @webapp2.cached_property 72 def jinja2(self): 73 # Returns a Jinja2 renderer cached in the app registry. 74 return jinja2.get_jinja2(app=self.app) 75 76 def render_response(self, _template, **context): 77 # Renders a template and writes the result to the response. 78 rv = self.jinja2.render_template(_template, **context) 79 self.response.write(rv) 80 81 Then extended handlers can render templates directly:: 82 83 class MyHandler(BaseHandler): 84 def get(self): 85 context = {'message': 'Hello, world!'} 86 self.render_response('my_template.html', **context) 87 """ 88 89 #: Configuration key. 90 config_key = __name__ 91 92 #: Loaded configuration. 93 config = None 94 95 def __init__(self, app, config=None): 96 """Initializes the Jinja2 object. 97 98 :param app: 99 A :class:`webapp2.WSGIApplication` instance. 100 :param config: 101 A dictionary of configuration values to be overridden. See 102 the available keys in :data:`default_config`. 103 """ 104 self.config = config = app.config.load_config(self.config_key, 105 default_values=default_config, user_values=config, 106 required_keys=None) 107 kwargs = config['environment_args'].copy() 108 enable_i18n = 'jinja2.ext.i18n' in kwargs.get('extensions', []) 109 110 if 'loader' not in kwargs: 111 template_path = config['template_path'] 112 compiled_path = config['compiled_path'] 113 use_compiled = not app.debug or config['force_compiled'] 114 115 if compiled_path and use_compiled: 116 # Use precompiled templates loaded from a module or zip. 117 kwargs['loader'] = jinja2.ModuleLoader(compiled_path) 118 else: 119 # Parse templates for every new environment instances. 120 kwargs['loader'] = jinja2.FileSystemLoader(template_path) 121 122 # Initialize the environment. 123 env = jinja2.Environment(**kwargs) 124 125 if config['globals']: 126 env.globals.update(config['globals']) 127 128 if config['filters']: 129 env.filters.update(config['filters']) 130 131 if enable_i18n: 132 # Install i18n. 133 from webapp2_extras import i18n 134 env.install_gettext_callables( 135 lambda x: i18n.gettext(x), 136 lambda s, p, n: i18n.ngettext(s, p, n), 137 newstyle=True) 138 env.filters.update({ 139 'format_date': i18n.format_date, 140 'format_time': i18n.format_time, 141 'format_datetime': i18n.format_datetime, 142 'format_timedelta': i18n.format_timedelta, 143 }) 144 145 self.environment = env 146 147 def render_template(self, _filename, **context): 148 """Renders a template and returns a response object. 149 150 :param _filename: 151 The template filename, related to the templates directory. 152 :param context: 153 Keyword arguments used as variables in the rendered template. 154 These will override values set in the request context. 155 :returns: 156 A rendered template. 157 """ 158 return self.environment.get_template(_filename).render(**context) 159 160 def get_template_attribute(self, filename, attribute): 161 """Loads a macro (or variable) a template exports. This can be used to 162 invoke a macro from within Python code. If you for example have a 163 template named `_foo.html` with the following contents: 164 165 .. sourcecode:: html+jinja 166 167 {% macro hello(name) %}Hello {{ name }}!{% endmacro %} 168 169 You can access this from Python code like this:: 170 171 hello = get_template_attribute('_foo.html', 'hello') 172 return hello('World') 173 174 This function comes from `Flask`. 175 176 :param filename: 177 The template filename. 178 :param attribute: 179 The name of the variable of macro to acccess. 180 """ 181 template = self.environment.get_template(filename) 182 return getattr(template.module, attribute) 183 184 185# Factories ------------------------------------------------------------------- 186 187 188#: Key used to store :class:`Jinja2` in the app registry. 189_registry_key = 'webapp2_extras.jinja2.Jinja2' 190 191 192def get_jinja2(factory=Jinja2, key=_registry_key, app=None): 193 """Returns an instance of :class:`Jinja2` from the app registry. 194 195 It'll try to get it from the current app registry, and if it is not 196 registered it'll be instantiated and registered. A second call to this 197 function will return the same instance. 198 199 :param factory: 200 The callable used to build and register the instance if it is not yet 201 registered. The default is the class :class:`Jinja2` itself. 202 :param key: 203 The key used to store the instance in the registry. A default is used 204 if it is not set. 205 :param app: 206 A :class:`webapp2.WSGIApplication` instance used to store the instance. 207 The active app is used if it is not set. 208 """ 209 app = app or webapp2.get_app() 210 jinja2 = app.registry.get(key) 211 if not jinja2: 212 jinja2 = app.registry[key] = factory(app) 213 214 return jinja2 215 216 217def set_jinja2(jinja2, key=_registry_key, app=None): 218 """Sets an instance of :class:`Jinja2` in the app registry. 219 220 :param store: 221 An instance of :class:`Jinja2`. 222 :param key: 223 The key used to retrieve the instance from the registry. A default 224 is used if it is not set. 225 :param request: 226 A :class:`webapp2.WSGIApplication` instance used to retrieve the 227 instance. The active app is used if it is not set. 228 """ 229 app = app or webapp2.get_app() 230 app.registry[key] = jinja2 231