| [1106] | 1 | .. -*- mode: rst; encoding: utf-8 -*- |
|---|
| 2 | |
|---|
| 3 | ================= |
|---|
| 4 | Loading Templates |
|---|
| 5 | ================= |
|---|
| 6 | |
|---|
| 7 | Genshi comes with a simple but flexible implementation of a template loader in |
|---|
| 8 | the ``genshi.template.loader`` module. The loader provides caching of |
|---|
| 9 | templates so they do not need to be reparsed when used, support for multiple |
|---|
| 10 | template directories that together form a virtual search path, as well as |
|---|
| 11 | support for different template loading strategies. |
|---|
| 12 | |
|---|
| 13 | .. contents:: Contents |
|---|
| 14 | :depth: 3 |
|---|
| 15 | .. sectnum:: |
|---|
| 16 | |
|---|
| 17 | |
|---|
| 18 | ----- |
|---|
| 19 | Usage |
|---|
| 20 | ----- |
|---|
| 21 | |
|---|
| 22 | The basic usage pattern is simple: instantiate one ``TemplateLoader`` object |
|---|
| 23 | and keep it around, then ask it to load a template whenever you need to load |
|---|
| 24 | one: |
|---|
| 25 | |
|---|
| [1107] | 26 | .. code-block:: python |
|---|
| [1106] | 27 | |
|---|
| [1107] | 28 | from genshi.template import TemplateLoader |
|---|
| [1106] | 29 | |
|---|
| [1107] | 30 | loader = TemplateLoader(['/path/to/dir1', '/path/to/dir2'], |
|---|
| 31 | auto_reload=True) |
|---|
| 32 | tmpl = loader.load('test.html') |
|---|
| [1106] | 33 | |
|---|
| 34 | When you try to load a template that can't be found, you get a |
|---|
| [1107] | 35 | ``TemplateNotFound`` error. |
|---|
| [1106] | 36 | |
|---|
| 37 | The default template class used by the loader is ``MarkupTemplate``, but that |
|---|
| 38 | can be overridden both with a different default (as a keyword argument to the |
|---|
| [1107] | 39 | ``TemplateLoader`` constructor), as well as on invocation of the ``load()`` |
|---|
| 40 | method: |
|---|
| [1106] | 41 | |
|---|
| [1107] | 42 | .. code-block:: python |
|---|
| [1106] | 43 | |
|---|
| [1107] | 44 | from genshi.template.text import NewTextTemplate |
|---|
| 45 | |
|---|
| 46 | tmpl = loader.load('mail.txt', cls=NewTextTemplate) |
|---|
| [1106] | 47 | |
|---|
| 48 | |
|---|
| 49 | ------- |
|---|
| 50 | Caching |
|---|
| 51 | ------- |
|---|
| 52 | |
|---|
| 53 | The ``TemplateLoader`` class provides a simple in-memory cache for parsed |
|---|
| 54 | template objects. This improves performance, because templates do not need to |
|---|
| 55 | be reparsed every time they are rendered. |
|---|
| 56 | |
|---|
| 57 | The size of this cache can be adjusted using the `max_cache_size` option on |
|---|
| 58 | the ``TemplateLoader`` constructor. The value of that option determines the |
|---|
| 59 | maximum number of template objects kept in the cache. When this limit is |
|---|
| 60 | reached, any templates that haven't been used in a while get purged. |
|---|
| [1107] | 61 | Technically, this is a least-recently-used (LRU) cache, the default limit is |
|---|
| 62 | set to 25 templates. |
|---|
| [1106] | 63 | |
|---|
| [1107] | 64 | Automatic Reloading |
|---|
| 65 | =================== |
|---|
| 66 | |
|---|
| [1106] | 67 | Once a template has been cached, it will normally not get reparsed until it |
|---|
| 68 | has been purged from the cache. This means that any changes to the template |
|---|
| [1107] | 69 | file are not taken into consideration as long as it is still found in the |
|---|
| [1106] | 70 | cache. As this is inconvenient in development scenarios, the ``auto_reload`` |
|---|
| 71 | option allows for automatic cache invalidation based on whether the template |
|---|
| [1107] | 72 | source has changed. |
|---|
| [1106] | 73 | |
|---|
| [1107] | 74 | .. code-block:: python |
|---|
| [1106] | 75 | |
|---|
| [1107] | 76 | from genshi.template import TemplateLoader |
|---|
| 77 | |
|---|
| 78 | loader = TemplateLoader('templates', auto_reload=True, max_cache_size=100) |
|---|
| 79 | |
|---|
| 80 | In production environments, automatic reloading should be disabled, as it does |
|---|
| 81 | affect performance negatively. |
|---|
| 82 | |
|---|
| 83 | Callback Interface |
|---|
| 84 | ================== |
|---|
| 85 | |
|---|
| 86 | Sometimes you need to make sure that templates get properly configured after |
|---|
| 87 | they have been loaded, but you only want to do that when the template is |
|---|
| 88 | actually loaded and parsed, not when it is returned from the cache. |
|---|
| 89 | |
|---|
| 90 | For such cases, the ``TemplateLoader`` provides a way to specify a callback |
|---|
| 91 | function that gets invoked whenever a template is loaded. You can specify that |
|---|
| 92 | callback by passing it into the loader constructor via the ``callback`` |
|---|
| 93 | keyword argument, or later by setting the attribute of the same name. The |
|---|
| 94 | callback function should expect a single argument, the template object. |
|---|
| 95 | |
|---|
| 96 | For example, to properly inject the `translation filter`_ into any loaded |
|---|
| 97 | template, you'd use code similar to this: |
|---|
| 98 | |
|---|
| 99 | .. code-block:: python |
|---|
| 100 | |
|---|
| 101 | from genshi.filters import Translator |
|---|
| 102 | from genshi.template import TemplateLoader |
|---|
| 103 | |
|---|
| 104 | def template_loaded(template): |
|---|
| [1108] | 105 | Translator(translations.ugettext).setup(template) |
|---|
| [1107] | 106 | |
|---|
| 107 | loader = TemplateLoader('templates', callback=template_loaded) |
|---|
| 108 | |
|---|
| 109 | .. _`translation filter`: i18n.html |
|---|
| 110 | |
|---|
| [1106] | 111 | -------------------- |
|---|
| 112 | Template Search Path |
|---|
| 113 | -------------------- |
|---|
| 114 | |
|---|
| [1107] | 115 | The template loader can be configured with a list of multiple directories to |
|---|
| 116 | search for templates. The loader maps these directories to a single logical |
|---|
| 117 | directory for locating templates by file name. |
|---|
| [1106] | 118 | |
|---|
| 119 | The order of the directories making up the search path is significant: the |
|---|
| [1107] | 120 | loader will first try to locate a requested template in the first directory on |
|---|
| 121 | the path, then in the second, and so on. If there are two templates with the |
|---|
| 122 | same file name in multiple directories on the search path, whatever file is |
|---|
| 123 | found first gets used. |
|---|
| [1106] | 124 | |
|---|
| 125 | Based on this design, an application could, for example, configure a search |
|---|
| 126 | path consisting of a directory containing the default templates, as well as a |
|---|
| 127 | directory where site-specific templates can be stored that will override the |
|---|
| 128 | default templates. |
|---|
| 129 | |
|---|
| 130 | |
|---|
| 131 | Load Functions |
|---|
| 132 | ============== |
|---|
| 133 | |
|---|
| 134 | Usually the search path consists of strings representing directory paths, but |
|---|
| 135 | it may also contain “load functions”: functions that are basically invoked |
|---|
| 136 | with the file name, and return the template content. |
|---|
| 137 | |
|---|
| 138 | Genshi comes with three builtin load functions: |
|---|
| 139 | |
|---|
| 140 | ``directory(path)`` |
|---|
| 141 | ------------------- |
|---|
| 142 | |
|---|
| 143 | The equivalent of just using a string containing the directory path: looks up |
|---|
| 144 | the file name in a specific directory. |
|---|
| 145 | |
|---|
| [1107] | 146 | .. code-block:: python |
|---|
| [1106] | 147 | |
|---|
| [1107] | 148 | from genshi.template import TemplateLoader, loader |
|---|
| 149 | tl = TemplateLoader([loader.directory('/path/to/dir/')]) |
|---|
| [1106] | 150 | |
|---|
| 151 | That is the same as: |
|---|
| 152 | |
|---|
| [1107] | 153 | .. code-block:: python |
|---|
| [1106] | 154 | |
|---|
| [1107] | 155 | tl = TemplateLoader(['/path/to/dir/']) |
|---|
| [1106] | 156 | |
|---|
| 157 | |
|---|
| 158 | ``package(name, path)`` |
|---|
| 159 | ----------------------- |
|---|
| 160 | |
|---|
| 161 | Uses the ``pkg_resources`` API to locate files in Python package data (which |
|---|
| 162 | may be inside a ZIP archive). |
|---|
| 163 | |
|---|
| [1107] | 164 | .. code-block:: python |
|---|
| [1106] | 165 | |
|---|
| [1107] | 166 | from genshi.template import TemplateLoader, loader |
|---|
| 167 | tl = TemplateLoader([loader.package('myapp', 'templates')]) |
|---|
| [1106] | 168 | |
|---|
| 169 | This will look for templates in the ``templates`` directory of the Python |
|---|
| 170 | package ``myapp``. |
|---|
| 171 | |
|---|
| 172 | ``prefixed(**delegates)`` |
|---|
| 173 | ------------------------- |
|---|
| 174 | |
|---|
| 175 | Delegates load requests to different load functions based on the path prefix. |
|---|
| 176 | |
|---|
| [1107] | 177 | .. code-block:: python |
|---|
| [1106] | 178 | |
|---|
| [1107] | 179 | from genshi.template import TemplateLoader, loader |
|---|
| 180 | tl = TemplateLoader(loader.prefixed( |
|---|
| 181 | core = '/tmp/dir1', |
|---|
| 182 | plugin1 = loader.package('plugin1', 'templates'), |
|---|
| 183 | plugin2 = loader.package('plugin2', 'templates'), |
|---|
| 184 | )) |
|---|
| 185 | tmpl = tl.load('core/index.html') |
|---|
| [1106] | 186 | |
|---|
| 187 | This example sets up a loader with three delegates, under the prefixes “core”, |
|---|
| [1107] | 188 | “plugin1”, and “plugin2”. When a template is requested, the ``prefixed`` load |
|---|
| [1106] | 189 | function looks for a delegate with a corresponding prefix, removes the prefix |
|---|
| 190 | from the path and asks the delegate to load the template. |
|---|
| 191 | |
|---|
| 192 | In this case, assuming the directory ``/path/to/dir`` contains a file named |
|---|
| 193 | ``index.html``, that file will be used when we load ``core/index.html``. The |
|---|
| [1107] | 194 | other delegates are not checked as their prefix does not match. |
|---|
| [1106] | 195 | |
|---|
| 196 | |
|---|
| 197 | .. note:: These builtin load functions are available both as class methods |
|---|
| 198 | of the ``TemplateLoader`` class as well as on the module level |
|---|
| 199 | |
|---|
| 200 | |
|---|
| 201 | Custom Load Functions |
|---|
| 202 | --------------------- |
|---|
| 203 | |
|---|
| 204 | You can easily use your own load function with the template loader, for |
|---|
| 205 | example to load templates from a database. All that is needed is a callable |
|---|
| 206 | object that accepts a ``filename`` (a string) and returns a tuple of the form |
|---|
| 207 | ``(filepath, filename, fileobj, uptodate_fun)``, where: |
|---|
| 208 | |
|---|
| 209 | ``filepath`` |
|---|
| [1107] | 210 | is the absolute path to the template. This is primarily used for output in |
|---|
| 211 | tracebacks, and does not need to map to an actual path on the file system. |
|---|
| [1106] | 212 | ``filename`` |
|---|
| 213 | is the base name of the template file |
|---|
| 214 | ``fileobj`` |
|---|
| 215 | is a readable file-like object that provides the content of the template |
|---|
| 216 | ``uptodate_fun`` |
|---|
| 217 | is a function that the loader can invoke to check whether the cached version |
|---|
| 218 | of the template is still up-to-date, or ``None`` if the load function is not |
|---|
| 219 | able to provide such a check. If provided, the function should not expect |
|---|
| 220 | any parameters (so you'll definitely want to use a closure here), and should |
|---|
| 221 | return ``True`` if the template has not changed since it was last loaded. |
|---|
| 222 | |
|---|
| 223 | When the requested template can not be found, the function should raise an |
|---|
| 224 | ``IOError`` or ``TemplateNotFound`` exception. |
|---|
| 225 | |
|---|
| 226 | |
|---|
| 227 | ------------------ |
|---|
| 228 | Customized Loading |
|---|
| 229 | ------------------ |
|---|
| 230 | |
|---|
| 231 | If you require a completely different implementation of template loading, you |
|---|
| 232 | can extend or even replace the builtin ``TemplateLoader`` class. |
|---|
| 233 | |
|---|
| 234 | Protocol |
|---|
| 235 | ======== |
|---|
| 236 | |
|---|
| [1107] | 237 | The protocol between the template loader and the ``Template`` class is simple |
|---|
| [1106] | 238 | and only used for processing includes. The only required part of that protocol |
|---|
| 239 | is that the object assigned to ``Template.loader`` implements a ``load`` |
|---|
| 240 | method compatible to that of the ``TemplateLoader`` class, at the minimum with |
|---|
| 241 | the signature ``load(filename, relative_to=None, cls=None)``. |
|---|
| 242 | |
|---|
| 243 | In addition, templates currently check for the existence and value of a boolean |
|---|
| [1113] | 244 | ``auto_reload`` property. If the property does not exist or evaluates to a |
|---|
| 245 | non-truth value, inlining of included templates is disabled. Inlining is a |
|---|
| 246 | small optimization that removes some overhead in the processing of includes. |
|---|
| [1106] | 247 | |
|---|
| 248 | Subclassing ``TemplateLoader`` |
|---|
| 249 | ============================== |
|---|
| 250 | |
|---|
| 251 | You can also adjust the behavior of the ``TemplateLoader`` class by subclassing |
|---|
| 252 | it. You can of course override anything needed, but the class also provides the |
|---|
| 253 | ``_instantiate()`` hook, which is intended for use by subclasses to customize |
|---|
| 254 | the creation of the template object from the file name and content. Please |
|---|
| 255 | consult the code and the API documentation for more detail. |
|---|