1b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)# -*- coding: utf-8 -*- 2b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)""" 3b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) jinja2.loaders 4b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) ~~~~~~~~~~~~~~ 5b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 6b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) Jinja loader classes. 7b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 8b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) :copyright: (c) 2010 by the Jinja Team. 9b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) :license: BSD, see LICENSE for more details. 10b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)""" 11b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)import os 12b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)import sys 13b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)import weakref 14b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)from types import ModuleType 15b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)from os import path 1658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)from hashlib import sha1 17b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)from jinja2.exceptions import TemplateNotFound 1858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)from jinja2.utils import open_if_exists, internalcode 1958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)from jinja2._compat import string_types, iteritems 20b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 21b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 22b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)def split_template_path(template): 23b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """Split a path into segments and perform a sanity check. If it detects 24b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) '..' in the path it will raise a `TemplateNotFound` error. 25b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """ 26b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) pieces = [] 27b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) for piece in template.split('/'): 28b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if path.sep in piece \ 29b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) or (path.altsep and path.altsep in piece) or \ 30b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) piece == path.pardir: 31b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) raise TemplateNotFound(template) 32b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) elif piece and piece != '.': 33b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) pieces.append(piece) 34b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return pieces 35b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 36b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 37b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)class BaseLoader(object): 38b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """Baseclass for all loaders. Subclass this and override `get_source` to 39b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) implement a custom loading mechanism. The environment provides a 40b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) `get_template` method that calls the loader's `load` method to get the 41b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) :class:`Template` object. 42b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 43b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) A very basic example for a loader that looks up templates on the file 44b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) system could look like this:: 45b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 46b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) from jinja2 import BaseLoader, TemplateNotFound 47b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) from os.path import join, exists, getmtime 48b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 49b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) class MyLoader(BaseLoader): 50b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 51b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def __init__(self, path): 52b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) self.path = path 53b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 54b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def get_source(self, environment, template): 55b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) path = join(self.path, template) 56b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if not exists(path): 57b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) raise TemplateNotFound(template) 58b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) mtime = getmtime(path) 59b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) with file(path) as f: 60b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) source = f.read().decode('utf-8') 61b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return source, path, lambda: mtime == getmtime(path) 62b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """ 63b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 64b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) #: if set to `False` it indicates that the loader cannot provide access 65b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) #: to the source of templates. 66b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) #: 67b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) #: .. versionadded:: 2.4 68b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) has_source_access = True 69b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 70b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def get_source(self, environment, template): 71b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """Get the template source, filename and reload helper for a template. 72b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) It's passed the environment and template name and has to return a 73b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) tuple in the form ``(source, filename, uptodate)`` or raise a 74b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) `TemplateNotFound` error if it can't locate the template. 75b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 76b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) The source part of the returned tuple must be the source of the 77b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) template as unicode string or a ASCII bytestring. The filename should 78b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) be the name of the file on the filesystem if it was loaded from there, 79b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) otherwise `None`. The filename is used by python for the tracebacks 80b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if no loader extension is used. 81b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 82b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) The last item in the tuple is the `uptodate` function. If auto 83b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) reloading is enabled it's always called to check if the template 84b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) changed. No arguments are passed so the function must store the 85b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) old state somewhere (for example in a closure). If it returns `False` 86b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) the template will be reloaded. 87b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """ 88b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if not self.has_source_access: 89b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) raise RuntimeError('%s cannot provide access to the source' % 90b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) self.__class__.__name__) 91b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) raise TemplateNotFound(template) 92b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 93b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def list_templates(self): 94b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """Iterates over all templates. If the loader does not support that 95b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) it should raise a :exc:`TypeError` which is the default behavior. 96b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """ 97b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) raise TypeError('this loader cannot iterate over all templates') 98b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 99b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) @internalcode 100b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def load(self, environment, name, globals=None): 101b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """Loads a template. This method looks up the template in the cache 102b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) or loads one by calling :meth:`get_source`. Subclasses should not 103b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) override this method as loaders working on collections of other 104b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) loaders (such as :class:`PrefixLoader` or :class:`ChoiceLoader`) 105b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) will not call this method but `get_source` directly. 106b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """ 107b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) code = None 108b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if globals is None: 109b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) globals = {} 110b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 111b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # first we try to get the source for this template together 112b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # with the filename and the uptodate function. 113b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) source, filename, uptodate = self.get_source(environment, name) 114b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 115b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # try to load the code from the bytecode cache if there is a 116b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # bytecode cache configured. 117b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) bcc = environment.bytecode_cache 118b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if bcc is not None: 119b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) bucket = bcc.get_bucket(environment, name, filename, source) 120b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) code = bucket.code 121b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 122b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # if we don't have code so far (not cached, no longer up to 123b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # date) etc. we compile the template 124b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if code is None: 125b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) code = environment.compile(source, name, filename) 126b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 127b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # if the bytecode cache is available and the bucket doesn't 128b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # have a code so far, we give the bucket the new code and put 129b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # it back to the bytecode cache. 130b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if bcc is not None and bucket.code is None: 131b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) bucket.code = code 132b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) bcc.set_bucket(bucket) 133b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 134b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return environment.template_class.from_code(environment, code, 135b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) globals, uptodate) 136b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 137b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 138b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)class FileSystemLoader(BaseLoader): 139b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """Loads templates from the file system. This loader can find templates 140b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) in folders on the file system and is the preferred way to load them. 141b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 142b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) The loader takes the path to the templates as string, or if multiple 143b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) locations are wanted a list of them which is then looked up in the 144b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) given order: 145b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 146b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) >>> loader = FileSystemLoader('/path/to/templates') 147b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) >>> loader = FileSystemLoader(['/path/to/templates', '/other/path']) 148b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 149b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) Per default the template encoding is ``'utf-8'`` which can be changed 150b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) by setting the `encoding` parameter to something else. 151b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """ 152b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 153b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def __init__(self, searchpath, encoding='utf-8'): 15458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) if isinstance(searchpath, string_types): 155b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) searchpath = [searchpath] 156b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) self.searchpath = list(searchpath) 157b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) self.encoding = encoding 158b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 159b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def get_source(self, environment, template): 160b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) pieces = split_template_path(template) 161b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) for searchpath in self.searchpath: 162b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) filename = path.join(searchpath, *pieces) 163b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) f = open_if_exists(filename) 164b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if f is None: 165b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) continue 166b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) try: 167b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) contents = f.read().decode(self.encoding) 168b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) finally: 169b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) f.close() 170b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 171b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) mtime = path.getmtime(filename) 172b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def uptodate(): 173b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) try: 174b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return path.getmtime(filename) == mtime 175b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) except OSError: 176b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return False 177b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return contents, filename, uptodate 178b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) raise TemplateNotFound(template) 179b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 180b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def list_templates(self): 181b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) found = set() 182b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) for searchpath in self.searchpath: 183b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) for dirpath, dirnames, filenames in os.walk(searchpath): 184b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) for filename in filenames: 185b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) template = os.path.join(dirpath, filename) \ 186b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) [len(searchpath):].strip(os.path.sep) \ 187b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) .replace(os.path.sep, '/') 188b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if template[:2] == './': 189b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) template = template[2:] 190b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if template not in found: 191b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) found.add(template) 192b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return sorted(found) 193b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 194b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 195b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)class PackageLoader(BaseLoader): 196b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """Load templates from python eggs or packages. It is constructed with 197b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) the name of the python package and the path to the templates in that 198b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) package:: 199b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 200b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) loader = PackageLoader('mypackage', 'views') 201b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 202b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) If the package path is not given, ``'templates'`` is assumed. 203b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 204b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) Per default the template encoding is ``'utf-8'`` which can be changed 205b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) by setting the `encoding` parameter to something else. Due to the nature 206b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) of eggs it's only possible to reload templates if the package was loaded 207b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) from the file system and not a zip file. 208b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """ 209b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 210b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def __init__(self, package_name, package_path='templates', 211b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) encoding='utf-8'): 212b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) from pkg_resources import DefaultProvider, ResourceManager, \ 213b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) get_provider 214b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) provider = get_provider(package_name) 215b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) self.encoding = encoding 216b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) self.manager = ResourceManager() 217b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) self.filesystem_bound = isinstance(provider, DefaultProvider) 218b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) self.provider = provider 219b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) self.package_path = package_path 220b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 221b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def get_source(self, environment, template): 222b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) pieces = split_template_path(template) 223b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) p = '/'.join((self.package_path,) + tuple(pieces)) 224b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if not self.provider.has_resource(p): 225b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) raise TemplateNotFound(template) 226b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 227b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) filename = uptodate = None 228b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if self.filesystem_bound: 229b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) filename = self.provider.get_resource_filename(self.manager, p) 230b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) mtime = path.getmtime(filename) 231b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def uptodate(): 232b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) try: 233b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return path.getmtime(filename) == mtime 234b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) except OSError: 235b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return False 236b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 237b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) source = self.provider.get_resource_string(self.manager, p) 238b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return source.decode(self.encoding), filename, uptodate 239b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 240b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def list_templates(self): 241b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) path = self.package_path 242b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if path[:2] == './': 243b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) path = path[2:] 244b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) elif path == '.': 245b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) path = '' 246b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) offset = len(path) 247b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) results = [] 248b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def _walk(path): 249b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) for filename in self.provider.resource_listdir(path): 250b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) fullname = path + '/' + filename 251b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if self.provider.resource_isdir(fullname): 252b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) _walk(fullname) 253b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) else: 254b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) results.append(fullname[offset:].lstrip('/')) 255b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) _walk(path) 256b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) results.sort() 257b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return results 258b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 259b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 260b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)class DictLoader(BaseLoader): 261b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """Loads a template from a python dict. It's passed a dict of unicode 262b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) strings bound to template names. This loader is useful for unittesting: 263b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 264b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) >>> loader = DictLoader({'index.html': 'source here'}) 265b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 266b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) Because auto reloading is rarely useful this is disabled per default. 267b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """ 268b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 269b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def __init__(self, mapping): 270b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) self.mapping = mapping 271b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 272b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def get_source(self, environment, template): 273b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if template in self.mapping: 274b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) source = self.mapping[template] 27558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) return source, None, lambda: source == self.mapping.get(template) 276b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) raise TemplateNotFound(template) 277b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 278b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def list_templates(self): 279b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return sorted(self.mapping) 280b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 281b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 282b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)class FunctionLoader(BaseLoader): 283b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """A loader that is passed a function which does the loading. The 284b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) function becomes the name of the template passed and has to return either 285b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) an unicode string with the template source, a tuple in the form ``(source, 286b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) filename, uptodatefunc)`` or `None` if the template does not exist. 287b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 288b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) >>> def load_template(name): 289b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) ... if name == 'index.html': 290b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) ... return '...' 291b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) ... 292b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) >>> loader = FunctionLoader(load_template) 293b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 294b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) The `uptodatefunc` is a function that is called if autoreload is enabled 295b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) and has to return `True` if the template is still up to date. For more 296b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) details have a look at :meth:`BaseLoader.get_source` which has the same 297b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return value. 298b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """ 299b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 300b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def __init__(self, load_func): 301b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) self.load_func = load_func 302b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 303b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def get_source(self, environment, template): 304b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) rv = self.load_func(template) 305b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if rv is None: 306b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) raise TemplateNotFound(template) 30758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) elif isinstance(rv, string_types): 308b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return rv, None, None 309b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return rv 310b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 311b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 312b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)class PrefixLoader(BaseLoader): 313b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """A loader that is passed a dict of loaders where each loader is bound 314b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) to a prefix. The prefix is delimited from the template by a slash per 315b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) default, which can be changed by setting the `delimiter` argument to 316b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) something else:: 317b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 318b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) loader = PrefixLoader({ 319b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 'app1': PackageLoader('mypackage.app1'), 320b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 'app2': PackageLoader('mypackage.app2') 321b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) }) 322b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 323b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) By loading ``'app1/index.html'`` the file from the app1 package is loaded, 324b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) by loading ``'app2/index.html'`` the file from the second. 325b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """ 326b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 327b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def __init__(self, mapping, delimiter='/'): 328b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) self.mapping = mapping 329b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) self.delimiter = delimiter 330b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 33158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) def get_loader(self, template): 332b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) try: 333b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) prefix, name = template.split(self.delimiter, 1) 334b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) loader = self.mapping[prefix] 335b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) except (ValueError, KeyError): 336b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) raise TemplateNotFound(template) 33758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) return loader, name 33858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) 33958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) def get_source(self, environment, template): 34058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) loader, name = self.get_loader(template) 341b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) try: 342b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return loader.get_source(environment, name) 343b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) except TemplateNotFound: 344b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # re-raise the exception with the correct fileame here. 345b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # (the one that includes the prefix) 346b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) raise TemplateNotFound(template) 347b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 34858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) @internalcode 34958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) def load(self, environment, name, globals=None): 35058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) loader, local_name = self.get_loader(name) 35158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) try: 35258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) return loader.load(environment, local_name) 35358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) except TemplateNotFound: 35458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) # re-raise the exception with the correct fileame here. 35558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) # (the one that includes the prefix) 35658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) raise TemplateNotFound(name) 35758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) 358b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def list_templates(self): 359b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) result = [] 36058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) for prefix, loader in iteritems(self.mapping): 361b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) for template in loader.list_templates(): 362b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) result.append(prefix + self.delimiter + template) 363b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return result 364b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 365b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 366b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)class ChoiceLoader(BaseLoader): 367b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """This loader works like the `PrefixLoader` just that no prefix is 368b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) specified. If a template could not be found by one loader the next one 369b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) is tried. 370b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 371b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) >>> loader = ChoiceLoader([ 372b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) ... FileSystemLoader('/path/to/user/templates'), 373b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) ... FileSystemLoader('/path/to/system/templates') 374b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) ... ]) 375b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 376b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) This is useful if you want to allow users to override builtin templates 377b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) from a different location. 378b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """ 379b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 380b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def __init__(self, loaders): 381b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) self.loaders = loaders 382b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 383b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def get_source(self, environment, template): 384b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) for loader in self.loaders: 385b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) try: 386b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return loader.get_source(environment, template) 387b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) except TemplateNotFound: 388b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) pass 389b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) raise TemplateNotFound(template) 390b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 39158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) @internalcode 39258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) def load(self, environment, name, globals=None): 39358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) for loader in self.loaders: 39458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) try: 39558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) return loader.load(environment, name, globals) 39658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) except TemplateNotFound: 39758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) pass 39858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) raise TemplateNotFound(name) 39958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) 400b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def list_templates(self): 401b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) found = set() 402b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) for loader in self.loaders: 403b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) found.update(loader.list_templates()) 404b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return sorted(found) 405b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 406b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 407b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)class _TemplateModule(ModuleType): 408b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """Like a normal module but with support for weak references""" 409b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 410b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 411b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)class ModuleLoader(BaseLoader): 412b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """This loader loads templates from precompiled templates. 413b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 414b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) Example usage: 415b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 416b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) >>> loader = ChoiceLoader([ 417b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) ... ModuleLoader('/path/to/compiled/templates'), 418b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) ... FileSystemLoader('/path/to/templates') 419b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) ... ]) 420b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 421b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) Templates can be precompiled with :meth:`Environment.compile_templates`. 422b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """ 423b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 424b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) has_source_access = False 425b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 426b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def __init__(self, path): 427b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) package_name = '_jinja2_module_templates_%x' % id(self) 428b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 429b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # create a fake module that looks for the templates in the 430b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # path given. 431b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) mod = _TemplateModule(package_name) 43258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) if isinstance(path, string_types): 433b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) path = [path] 434b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) else: 435b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) path = list(path) 436b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) mod.__path__ = path 437b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 438b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) sys.modules[package_name] = weakref.proxy(mod, 439b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) lambda x: sys.modules.pop(package_name, None)) 440b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 441b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # the only strong reference, the sys.modules entry is weak 442b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # so that the garbage collector can remove it once the 443b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # loader that created it goes out of business. 444b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) self.module = mod 445b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) self.package_name = package_name 446b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 447b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) @staticmethod 448b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def get_template_key(name): 449b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return 'tmpl_' + sha1(name.encode('utf-8')).hexdigest() 450b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 451b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) @staticmethod 452b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def get_module_filename(name): 453b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return ModuleLoader.get_template_key(name) + '.py' 454b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 455b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) @internalcode 456b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def load(self, environment, name, globals=None): 457b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) key = self.get_template_key(name) 458b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) module = '%s.%s' % (self.package_name, key) 459b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) mod = getattr(self.module, module, None) 460b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if mod is None: 461b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) try: 462b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) mod = __import__(module, None, None, ['root']) 463b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) except ImportError: 464b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) raise TemplateNotFound(name) 465b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 466b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # remove the entry from sys.modules, we only want the attribute 467b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # on the module object we have stored on the loader. 468b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) sys.modules.pop(module, None) 469b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 470b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return environment.template_class.from_module_dict( 471b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) environment, mod.__dict__, globals) 472