cmlenz@883: .. -*- mode: rst; encoding: utf-8 -*- cmlenz@883: cmlenz@883: ================= cmlenz@883: Loading Templates cmlenz@883: ================= cmlenz@883: cmlenz@883: Genshi comes with a simple but flexible implementation of a template loader in cmlenz@883: the ``genshi.template.loader`` module. The loader provides caching of cmlenz@883: templates so they do not need to be reparsed when used, support for multiple cmlenz@883: template directories that together form a virtual search path, as well as cmlenz@883: support for different template loading strategies. cmlenz@883: cmlenz@883: .. contents:: Contents cmlenz@883: :depth: 3 cmlenz@883: .. sectnum:: cmlenz@883: cmlenz@883: cmlenz@883: ----- cmlenz@883: Usage cmlenz@883: ----- cmlenz@883: cmlenz@883: The basic usage pattern is simple: instantiate one ``TemplateLoader`` object cmlenz@883: and keep it around, then ask it to load a template whenever you need to load cmlenz@883: one: cmlenz@883: cmlenz@884: .. code-block:: python cmlenz@883: cmlenz@884: from genshi.template import TemplateLoader cmlenz@883: cmlenz@884: loader = TemplateLoader(['/path/to/dir1', '/path/to/dir2'], cmlenz@884: auto_reload=True) cmlenz@884: tmpl = loader.load('test.html') cmlenz@883: cmlenz@883: When you try to load a template that can't be found, you get a cmlenz@884: ``TemplateNotFound`` error. cmlenz@883: cmlenz@883: The default template class used by the loader is ``MarkupTemplate``, but that cmlenz@883: can be overridden both with a different default (as a keyword argument to the cmlenz@884: ``TemplateLoader`` constructor), as well as on invocation of the ``load()`` cmlenz@884: method: cmlenz@883: cmlenz@884: .. code-block:: python cmlenz@883: cmlenz@884: from genshi.template.text import NewTextTemplate cmlenz@884: cmlenz@884: tmpl = loader.load('mail.txt', cls=NewTextTemplate) cmlenz@883: cmlenz@883: cmlenz@883: ------- cmlenz@883: Caching cmlenz@883: ------- cmlenz@883: cmlenz@883: The ``TemplateLoader`` class provides a simple in-memory cache for parsed cmlenz@883: template objects. This improves performance, because templates do not need to cmlenz@883: be reparsed every time they are rendered. cmlenz@883: cmlenz@883: The size of this cache can be adjusted using the `max_cache_size` option on cmlenz@883: the ``TemplateLoader`` constructor. The value of that option determines the cmlenz@883: maximum number of template objects kept in the cache. When this limit is cmlenz@883: reached, any templates that haven't been used in a while get purged. cmlenz@884: Technically, this is a least-recently-used (LRU) cache, the default limit is cmlenz@884: set to 25 templates. cmlenz@884: cmlenz@884: Automatic Reloading cmlenz@884: =================== cmlenz@883: cmlenz@883: Once a template has been cached, it will normally not get reparsed until it cmlenz@883: has been purged from the cache. This means that any changes to the template cmlenz@884: file are not taken into consideration as long as it is still found in the cmlenz@883: cache. As this is inconvenient in development scenarios, the ``auto_reload`` cmlenz@883: option allows for automatic cache invalidation based on whether the template cmlenz@884: source has changed. cmlenz@883: cmlenz@884: .. code-block:: python cmlenz@884: cmlenz@884: from genshi.template import TemplateLoader cmlenz@884: cmlenz@884: loader = TemplateLoader('templates', auto_reload=True, max_cache_size=100) cmlenz@884: cmlenz@884: In production environments, automatic reloading should be disabled, as it does cmlenz@884: affect performance negatively. cmlenz@884: cmlenz@884: Callback Interface cmlenz@884: ================== cmlenz@884: cmlenz@884: Sometimes you need to make sure that templates get properly configured after cmlenz@884: they have been loaded, but you only want to do that when the template is cmlenz@884: actually loaded and parsed, not when it is returned from the cache. cmlenz@884: cmlenz@884: For such cases, the ``TemplateLoader`` provides a way to specify a callback cmlenz@884: function that gets invoked whenever a template is loaded. You can specify that cmlenz@884: callback by passing it into the loader constructor via the ``callback`` cmlenz@884: keyword argument, or later by setting the attribute of the same name. The cmlenz@884: callback function should expect a single argument, the template object. cmlenz@884: cmlenz@884: For example, to properly inject the `translation filter`_ into any loaded cmlenz@884: template, you'd use code similar to this: cmlenz@884: cmlenz@884: .. code-block:: python cmlenz@884: cmlenz@884: from genshi.filters import Translator cmlenz@884: from genshi.template import TemplateLoader cmlenz@884: cmlenz@884: def template_loaded(template): cmlenz@885: Translator(translations.ugettext).setup(template) cmlenz@884: cmlenz@884: loader = TemplateLoader('templates', callback=template_loaded) cmlenz@884: cmlenz@884: .. _`translation filter`: i18n.html cmlenz@883: cmlenz@883: -------------------- cmlenz@883: Template Search Path cmlenz@883: -------------------- cmlenz@883: cmlenz@884: The template loader can be configured with a list of multiple directories to cmlenz@884: search for templates. The loader maps these directories to a single logical cmlenz@884: directory for locating templates by file name. cmlenz@883: cmlenz@883: The order of the directories making up the search path is significant: the cmlenz@884: loader will first try to locate a requested template in the first directory on cmlenz@884: the path, then in the second, and so on. If there are two templates with the cmlenz@884: same file name in multiple directories on the search path, whatever file is cmlenz@884: found first gets used. cmlenz@883: cmlenz@883: Based on this design, an application could, for example, configure a search cmlenz@883: path consisting of a directory containing the default templates, as well as a cmlenz@883: directory where site-specific templates can be stored that will override the cmlenz@883: default templates. cmlenz@883: cmlenz@883: cmlenz@883: Load Functions cmlenz@883: ============== cmlenz@883: cmlenz@883: Usually the search path consists of strings representing directory paths, but cmlenz@883: it may also contain “load functions”: functions that are basically invoked cmlenz@883: with the file name, and return the template content. cmlenz@883: cmlenz@883: Genshi comes with three builtin load functions: cmlenz@883: cmlenz@883: ``directory(path)`` cmlenz@883: ------------------- cmlenz@883: cmlenz@883: The equivalent of just using a string containing the directory path: looks up cmlenz@883: the file name in a specific directory. cmlenz@883: cmlenz@884: .. code-block:: python cmlenz@883: cmlenz@884: from genshi.template import TemplateLoader, loader cmlenz@884: tl = TemplateLoader([loader.directory('/path/to/dir/')]) cmlenz@883: cmlenz@883: That is the same as: cmlenz@883: cmlenz@884: .. code-block:: python cmlenz@883: cmlenz@884: tl = TemplateLoader(['/path/to/dir/']) cmlenz@883: cmlenz@883: cmlenz@883: ``package(name, path)`` cmlenz@883: ----------------------- cmlenz@883: cmlenz@883: Uses the ``pkg_resources`` API to locate files in Python package data (which cmlenz@883: may be inside a ZIP archive). cmlenz@883: cmlenz@884: .. code-block:: python cmlenz@883: cmlenz@884: from genshi.template import TemplateLoader, loader cmlenz@884: tl = TemplateLoader([loader.package('myapp', 'templates')]) cmlenz@883: cmlenz@883: This will look for templates in the ``templates`` directory of the Python cmlenz@883: package ``myapp``. cmlenz@883: cmlenz@883: ``prefixed(**delegates)`` cmlenz@883: ------------------------- cmlenz@883: cmlenz@883: Delegates load requests to different load functions based on the path prefix. cmlenz@883: cmlenz@884: .. code-block:: python cmlenz@883: cmlenz@884: from genshi.template import TemplateLoader, loader cmlenz@884: tl = TemplateLoader(loader.prefixed( cmlenz@884: core = '/tmp/dir1', cmlenz@884: plugin1 = loader.package('plugin1', 'templates'), cmlenz@884: plugin2 = loader.package('plugin2', 'templates'), cmlenz@884: )) cmlenz@884: tmpl = tl.load('core/index.html') cmlenz@883: cmlenz@883: This example sets up a loader with three delegates, under the prefixes “core”, cmlenz@884: “plugin1”, and “plugin2”. When a template is requested, the ``prefixed`` load cmlenz@883: function looks for a delegate with a corresponding prefix, removes the prefix cmlenz@883: from the path and asks the delegate to load the template. cmlenz@883: cmlenz@883: In this case, assuming the directory ``/path/to/dir`` contains a file named cmlenz@883: ``index.html``, that file will be used when we load ``core/index.html``. The cmlenz@884: other delegates are not checked as their prefix does not match. cmlenz@883: cmlenz@883: cmlenz@883: .. note:: These builtin load functions are available both as class methods cmlenz@883: of the ``TemplateLoader`` class as well as on the module level cmlenz@883: cmlenz@883: cmlenz@883: Custom Load Functions cmlenz@883: --------------------- cmlenz@883: cmlenz@883: You can easily use your own load function with the template loader, for cmlenz@883: example to load templates from a database. All that is needed is a callable cmlenz@883: object that accepts a ``filename`` (a string) and returns a tuple of the form cmlenz@883: ``(filepath, filename, fileobj, uptodate_fun)``, where: cmlenz@883: cmlenz@883: ``filepath`` cmlenz@884: is the absolute path to the template. This is primarily used for output in cmlenz@884: tracebacks, and does not need to map to an actual path on the file system. cmlenz@883: ``filename`` cmlenz@883: is the base name of the template file cmlenz@883: ``fileobj`` cmlenz@883: is a readable file-like object that provides the content of the template cmlenz@883: ``uptodate_fun`` cmlenz@883: is a function that the loader can invoke to check whether the cached version cmlenz@883: of the template is still up-to-date, or ``None`` if the load function is not cmlenz@883: able to provide such a check. If provided, the function should not expect cmlenz@883: any parameters (so you'll definitely want to use a closure here), and should cmlenz@883: return ``True`` if the template has not changed since it was last loaded. cmlenz@883: cmlenz@883: When the requested template can not be found, the function should raise an cmlenz@883: ``IOError`` or ``TemplateNotFound`` exception. cmlenz@883: cmlenz@883: cmlenz@883: ------------------ cmlenz@883: Customized Loading cmlenz@883: ------------------ cmlenz@883: cmlenz@883: If you require a completely different implementation of template loading, you cmlenz@883: can extend or even replace the builtin ``TemplateLoader`` class. cmlenz@883: cmlenz@883: Protocol cmlenz@883: ======== cmlenz@883: cmlenz@884: The protocol between the template loader and the ``Template`` class is simple cmlenz@883: and only used for processing includes. The only required part of that protocol cmlenz@883: is that the object assigned to ``Template.loader`` implements a ``load`` cmlenz@883: method compatible to that of the ``TemplateLoader`` class, at the minimum with cmlenz@883: the signature ``load(filename, relative_to=None, cls=None)``. cmlenz@883: cmlenz@883: In addition, templates currently check for the existence and value of a boolean cmlenz@890: ``auto_reload`` property. If the property does not exist or evaluates to a cmlenz@890: non-truth value, inlining of included templates is disabled. Inlining is a cmlenz@890: small optimization that removes some overhead in the processing of includes. cmlenz@883: cmlenz@883: Subclassing ``TemplateLoader`` cmlenz@883: ============================== cmlenz@883: cmlenz@883: You can also adjust the behavior of the ``TemplateLoader`` class by subclassing cmlenz@883: it. You can of course override anything needed, but the class also provides the cmlenz@883: ``_instantiate()`` hook, which is intended for use by subclasses to customize cmlenz@883: the creation of the template object from the file name and content. Please cmlenz@883: consult the code and the API documentation for more detail.