11cb0ef41Sopenharmony_ci# -*- coding: utf-8 -*- 21cb0ef41Sopenharmony_ci""" 31cb0ef41Sopenharmony_ci jinja2.loaders 41cb0ef41Sopenharmony_ci ~~~~~~~~~~~~~~ 51cb0ef41Sopenharmony_ci 61cb0ef41Sopenharmony_ci Jinja loader classes. 71cb0ef41Sopenharmony_ci 81cb0ef41Sopenharmony_ci :copyright: (c) 2017 by the Jinja Team. 91cb0ef41Sopenharmony_ci :license: BSD, see LICENSE for more details. 101cb0ef41Sopenharmony_ci""" 111cb0ef41Sopenharmony_ciimport os 121cb0ef41Sopenharmony_ciimport sys 131cb0ef41Sopenharmony_ciimport weakref 141cb0ef41Sopenharmony_cifrom types import ModuleType 151cb0ef41Sopenharmony_cifrom os import path 161cb0ef41Sopenharmony_cifrom hashlib import sha1 171cb0ef41Sopenharmony_cifrom jinja2.exceptions import TemplateNotFound 181cb0ef41Sopenharmony_cifrom jinja2.utils import open_if_exists, internalcode 191cb0ef41Sopenharmony_cifrom jinja2._compat import string_types, iteritems 201cb0ef41Sopenharmony_ci 211cb0ef41Sopenharmony_ci 221cb0ef41Sopenharmony_cidef split_template_path(template): 231cb0ef41Sopenharmony_ci """Split a path into segments and perform a sanity check. If it detects 241cb0ef41Sopenharmony_ci '..' in the path it will raise a `TemplateNotFound` error. 251cb0ef41Sopenharmony_ci """ 261cb0ef41Sopenharmony_ci pieces = [] 271cb0ef41Sopenharmony_ci for piece in template.split('/'): 281cb0ef41Sopenharmony_ci if path.sep in piece \ 291cb0ef41Sopenharmony_ci or (path.altsep and path.altsep in piece) or \ 301cb0ef41Sopenharmony_ci piece == path.pardir: 311cb0ef41Sopenharmony_ci raise TemplateNotFound(template) 321cb0ef41Sopenharmony_ci elif piece and piece != '.': 331cb0ef41Sopenharmony_ci pieces.append(piece) 341cb0ef41Sopenharmony_ci return pieces 351cb0ef41Sopenharmony_ci 361cb0ef41Sopenharmony_ci 371cb0ef41Sopenharmony_ciclass BaseLoader(object): 381cb0ef41Sopenharmony_ci """Baseclass for all loaders. Subclass this and override `get_source` to 391cb0ef41Sopenharmony_ci implement a custom loading mechanism. The environment provides a 401cb0ef41Sopenharmony_ci `get_template` method that calls the loader's `load` method to get the 411cb0ef41Sopenharmony_ci :class:`Template` object. 421cb0ef41Sopenharmony_ci 431cb0ef41Sopenharmony_ci A very basic example for a loader that looks up templates on the file 441cb0ef41Sopenharmony_ci system could look like this:: 451cb0ef41Sopenharmony_ci 461cb0ef41Sopenharmony_ci from jinja2 import BaseLoader, TemplateNotFound 471cb0ef41Sopenharmony_ci from os.path import join, exists, getmtime 481cb0ef41Sopenharmony_ci 491cb0ef41Sopenharmony_ci class MyLoader(BaseLoader): 501cb0ef41Sopenharmony_ci 511cb0ef41Sopenharmony_ci def __init__(self, path): 521cb0ef41Sopenharmony_ci self.path = path 531cb0ef41Sopenharmony_ci 541cb0ef41Sopenharmony_ci def get_source(self, environment, template): 551cb0ef41Sopenharmony_ci path = join(self.path, template) 561cb0ef41Sopenharmony_ci if not exists(path): 571cb0ef41Sopenharmony_ci raise TemplateNotFound(template) 581cb0ef41Sopenharmony_ci mtime = getmtime(path) 591cb0ef41Sopenharmony_ci with file(path) as f: 601cb0ef41Sopenharmony_ci source = f.read().decode('utf-8') 611cb0ef41Sopenharmony_ci return source, path, lambda: mtime == getmtime(path) 621cb0ef41Sopenharmony_ci """ 631cb0ef41Sopenharmony_ci 641cb0ef41Sopenharmony_ci #: if set to `False` it indicates that the loader cannot provide access 651cb0ef41Sopenharmony_ci #: to the source of templates. 661cb0ef41Sopenharmony_ci #: 671cb0ef41Sopenharmony_ci #: .. versionadded:: 2.4 681cb0ef41Sopenharmony_ci has_source_access = True 691cb0ef41Sopenharmony_ci 701cb0ef41Sopenharmony_ci def get_source(self, environment, template): 711cb0ef41Sopenharmony_ci """Get the template source, filename and reload helper for a template. 721cb0ef41Sopenharmony_ci It's passed the environment and template name and has to return a 731cb0ef41Sopenharmony_ci tuple in the form ``(source, filename, uptodate)`` or raise a 741cb0ef41Sopenharmony_ci `TemplateNotFound` error if it can't locate the template. 751cb0ef41Sopenharmony_ci 761cb0ef41Sopenharmony_ci The source part of the returned tuple must be the source of the 771cb0ef41Sopenharmony_ci template as unicode string or a ASCII bytestring. The filename should 781cb0ef41Sopenharmony_ci be the name of the file on the filesystem if it was loaded from there, 791cb0ef41Sopenharmony_ci otherwise `None`. The filename is used by python for the tracebacks 801cb0ef41Sopenharmony_ci if no loader extension is used. 811cb0ef41Sopenharmony_ci 821cb0ef41Sopenharmony_ci The last item in the tuple is the `uptodate` function. If auto 831cb0ef41Sopenharmony_ci reloading is enabled it's always called to check if the template 841cb0ef41Sopenharmony_ci changed. No arguments are passed so the function must store the 851cb0ef41Sopenharmony_ci old state somewhere (for example in a closure). If it returns `False` 861cb0ef41Sopenharmony_ci the template will be reloaded. 871cb0ef41Sopenharmony_ci """ 881cb0ef41Sopenharmony_ci if not self.has_source_access: 891cb0ef41Sopenharmony_ci raise RuntimeError('%s cannot provide access to the source' % 901cb0ef41Sopenharmony_ci self.__class__.__name__) 911cb0ef41Sopenharmony_ci raise TemplateNotFound(template) 921cb0ef41Sopenharmony_ci 931cb0ef41Sopenharmony_ci def list_templates(self): 941cb0ef41Sopenharmony_ci """Iterates over all templates. If the loader does not support that 951cb0ef41Sopenharmony_ci it should raise a :exc:`TypeError` which is the default behavior. 961cb0ef41Sopenharmony_ci """ 971cb0ef41Sopenharmony_ci raise TypeError('this loader cannot iterate over all templates') 981cb0ef41Sopenharmony_ci 991cb0ef41Sopenharmony_ci @internalcode 1001cb0ef41Sopenharmony_ci def load(self, environment, name, globals=None): 1011cb0ef41Sopenharmony_ci """Loads a template. This method looks up the template in the cache 1021cb0ef41Sopenharmony_ci or loads one by calling :meth:`get_source`. Subclasses should not 1031cb0ef41Sopenharmony_ci override this method as loaders working on collections of other 1041cb0ef41Sopenharmony_ci loaders (such as :class:`PrefixLoader` or :class:`ChoiceLoader`) 1051cb0ef41Sopenharmony_ci will not call this method but `get_source` directly. 1061cb0ef41Sopenharmony_ci """ 1071cb0ef41Sopenharmony_ci code = None 1081cb0ef41Sopenharmony_ci if globals is None: 1091cb0ef41Sopenharmony_ci globals = {} 1101cb0ef41Sopenharmony_ci 1111cb0ef41Sopenharmony_ci # first we try to get the source for this template together 1121cb0ef41Sopenharmony_ci # with the filename and the uptodate function. 1131cb0ef41Sopenharmony_ci source, filename, uptodate = self.get_source(environment, name) 1141cb0ef41Sopenharmony_ci 1151cb0ef41Sopenharmony_ci # try to load the code from the bytecode cache if there is a 1161cb0ef41Sopenharmony_ci # bytecode cache configured. 1171cb0ef41Sopenharmony_ci bcc = environment.bytecode_cache 1181cb0ef41Sopenharmony_ci if bcc is not None: 1191cb0ef41Sopenharmony_ci bucket = bcc.get_bucket(environment, name, filename, source) 1201cb0ef41Sopenharmony_ci code = bucket.code 1211cb0ef41Sopenharmony_ci 1221cb0ef41Sopenharmony_ci # if we don't have code so far (not cached, no longer up to 1231cb0ef41Sopenharmony_ci # date) etc. we compile the template 1241cb0ef41Sopenharmony_ci if code is None: 1251cb0ef41Sopenharmony_ci code = environment.compile(source, name, filename) 1261cb0ef41Sopenharmony_ci 1271cb0ef41Sopenharmony_ci # if the bytecode cache is available and the bucket doesn't 1281cb0ef41Sopenharmony_ci # have a code so far, we give the bucket the new code and put 1291cb0ef41Sopenharmony_ci # it back to the bytecode cache. 1301cb0ef41Sopenharmony_ci if bcc is not None and bucket.code is None: 1311cb0ef41Sopenharmony_ci bucket.code = code 1321cb0ef41Sopenharmony_ci bcc.set_bucket(bucket) 1331cb0ef41Sopenharmony_ci 1341cb0ef41Sopenharmony_ci return environment.template_class.from_code(environment, code, 1351cb0ef41Sopenharmony_ci globals, uptodate) 1361cb0ef41Sopenharmony_ci 1371cb0ef41Sopenharmony_ci 1381cb0ef41Sopenharmony_ciclass FileSystemLoader(BaseLoader): 1391cb0ef41Sopenharmony_ci """Loads templates from the file system. This loader can find templates 1401cb0ef41Sopenharmony_ci in folders on the file system and is the preferred way to load them. 1411cb0ef41Sopenharmony_ci 1421cb0ef41Sopenharmony_ci The loader takes the path to the templates as string, or if multiple 1431cb0ef41Sopenharmony_ci locations are wanted a list of them which is then looked up in the 1441cb0ef41Sopenharmony_ci given order:: 1451cb0ef41Sopenharmony_ci 1461cb0ef41Sopenharmony_ci >>> loader = FileSystemLoader('/path/to/templates') 1471cb0ef41Sopenharmony_ci >>> loader = FileSystemLoader(['/path/to/templates', '/other/path']) 1481cb0ef41Sopenharmony_ci 1491cb0ef41Sopenharmony_ci Per default the template encoding is ``'utf-8'`` which can be changed 1501cb0ef41Sopenharmony_ci by setting the `encoding` parameter to something else. 1511cb0ef41Sopenharmony_ci 1521cb0ef41Sopenharmony_ci To follow symbolic links, set the *followlinks* parameter to ``True``:: 1531cb0ef41Sopenharmony_ci 1541cb0ef41Sopenharmony_ci >>> loader = FileSystemLoader('/path/to/templates', followlinks=True) 1551cb0ef41Sopenharmony_ci 1561cb0ef41Sopenharmony_ci .. versionchanged:: 2.8+ 1571cb0ef41Sopenharmony_ci The *followlinks* parameter was added. 1581cb0ef41Sopenharmony_ci """ 1591cb0ef41Sopenharmony_ci 1601cb0ef41Sopenharmony_ci def __init__(self, searchpath, encoding='utf-8', followlinks=False): 1611cb0ef41Sopenharmony_ci if isinstance(searchpath, string_types): 1621cb0ef41Sopenharmony_ci searchpath = [searchpath] 1631cb0ef41Sopenharmony_ci self.searchpath = list(searchpath) 1641cb0ef41Sopenharmony_ci self.encoding = encoding 1651cb0ef41Sopenharmony_ci self.followlinks = followlinks 1661cb0ef41Sopenharmony_ci 1671cb0ef41Sopenharmony_ci def get_source(self, environment, template): 1681cb0ef41Sopenharmony_ci pieces = split_template_path(template) 1691cb0ef41Sopenharmony_ci for searchpath in self.searchpath: 1701cb0ef41Sopenharmony_ci filename = path.join(searchpath, *pieces) 1711cb0ef41Sopenharmony_ci f = open_if_exists(filename) 1721cb0ef41Sopenharmony_ci if f is None: 1731cb0ef41Sopenharmony_ci continue 1741cb0ef41Sopenharmony_ci try: 1751cb0ef41Sopenharmony_ci contents = f.read().decode(self.encoding) 1761cb0ef41Sopenharmony_ci finally: 1771cb0ef41Sopenharmony_ci f.close() 1781cb0ef41Sopenharmony_ci 1791cb0ef41Sopenharmony_ci mtime = path.getmtime(filename) 1801cb0ef41Sopenharmony_ci 1811cb0ef41Sopenharmony_ci def uptodate(): 1821cb0ef41Sopenharmony_ci try: 1831cb0ef41Sopenharmony_ci return path.getmtime(filename) == mtime 1841cb0ef41Sopenharmony_ci except OSError: 1851cb0ef41Sopenharmony_ci return False 1861cb0ef41Sopenharmony_ci return contents, filename, uptodate 1871cb0ef41Sopenharmony_ci raise TemplateNotFound(template) 1881cb0ef41Sopenharmony_ci 1891cb0ef41Sopenharmony_ci def list_templates(self): 1901cb0ef41Sopenharmony_ci found = set() 1911cb0ef41Sopenharmony_ci for searchpath in self.searchpath: 1921cb0ef41Sopenharmony_ci walk_dir = os.walk(searchpath, followlinks=self.followlinks) 1931cb0ef41Sopenharmony_ci for dirpath, dirnames, filenames in walk_dir: 1941cb0ef41Sopenharmony_ci for filename in filenames: 1951cb0ef41Sopenharmony_ci template = os.path.join(dirpath, filename) \ 1961cb0ef41Sopenharmony_ci [len(searchpath):].strip(os.path.sep) \ 1971cb0ef41Sopenharmony_ci .replace(os.path.sep, '/') 1981cb0ef41Sopenharmony_ci if template[:2] == './': 1991cb0ef41Sopenharmony_ci template = template[2:] 2001cb0ef41Sopenharmony_ci if template not in found: 2011cb0ef41Sopenharmony_ci found.add(template) 2021cb0ef41Sopenharmony_ci return sorted(found) 2031cb0ef41Sopenharmony_ci 2041cb0ef41Sopenharmony_ci 2051cb0ef41Sopenharmony_ciclass PackageLoader(BaseLoader): 2061cb0ef41Sopenharmony_ci """Load templates from python eggs or packages. It is constructed with 2071cb0ef41Sopenharmony_ci the name of the python package and the path to the templates in that 2081cb0ef41Sopenharmony_ci package:: 2091cb0ef41Sopenharmony_ci 2101cb0ef41Sopenharmony_ci loader = PackageLoader('mypackage', 'views') 2111cb0ef41Sopenharmony_ci 2121cb0ef41Sopenharmony_ci If the package path is not given, ``'templates'`` is assumed. 2131cb0ef41Sopenharmony_ci 2141cb0ef41Sopenharmony_ci Per default the template encoding is ``'utf-8'`` which can be changed 2151cb0ef41Sopenharmony_ci by setting the `encoding` parameter to something else. Due to the nature 2161cb0ef41Sopenharmony_ci of eggs it's only possible to reload templates if the package was loaded 2171cb0ef41Sopenharmony_ci from the file system and not a zip file. 2181cb0ef41Sopenharmony_ci """ 2191cb0ef41Sopenharmony_ci 2201cb0ef41Sopenharmony_ci def __init__(self, package_name, package_path='templates', 2211cb0ef41Sopenharmony_ci encoding='utf-8'): 2221cb0ef41Sopenharmony_ci from pkg_resources import DefaultProvider, ResourceManager, \ 2231cb0ef41Sopenharmony_ci get_provider 2241cb0ef41Sopenharmony_ci provider = get_provider(package_name) 2251cb0ef41Sopenharmony_ci self.encoding = encoding 2261cb0ef41Sopenharmony_ci self.manager = ResourceManager() 2271cb0ef41Sopenharmony_ci self.filesystem_bound = isinstance(provider, DefaultProvider) 2281cb0ef41Sopenharmony_ci self.provider = provider 2291cb0ef41Sopenharmony_ci self.package_path = package_path 2301cb0ef41Sopenharmony_ci 2311cb0ef41Sopenharmony_ci def get_source(self, environment, template): 2321cb0ef41Sopenharmony_ci pieces = split_template_path(template) 2331cb0ef41Sopenharmony_ci p = '/'.join((self.package_path,) + tuple(pieces)) 2341cb0ef41Sopenharmony_ci if not self.provider.has_resource(p): 2351cb0ef41Sopenharmony_ci raise TemplateNotFound(template) 2361cb0ef41Sopenharmony_ci 2371cb0ef41Sopenharmony_ci filename = uptodate = None 2381cb0ef41Sopenharmony_ci if self.filesystem_bound: 2391cb0ef41Sopenharmony_ci filename = self.provider.get_resource_filename(self.manager, p) 2401cb0ef41Sopenharmony_ci mtime = path.getmtime(filename) 2411cb0ef41Sopenharmony_ci def uptodate(): 2421cb0ef41Sopenharmony_ci try: 2431cb0ef41Sopenharmony_ci return path.getmtime(filename) == mtime 2441cb0ef41Sopenharmony_ci except OSError: 2451cb0ef41Sopenharmony_ci return False 2461cb0ef41Sopenharmony_ci 2471cb0ef41Sopenharmony_ci source = self.provider.get_resource_string(self.manager, p) 2481cb0ef41Sopenharmony_ci return source.decode(self.encoding), filename, uptodate 2491cb0ef41Sopenharmony_ci 2501cb0ef41Sopenharmony_ci def list_templates(self): 2511cb0ef41Sopenharmony_ci path = self.package_path 2521cb0ef41Sopenharmony_ci if path[:2] == './': 2531cb0ef41Sopenharmony_ci path = path[2:] 2541cb0ef41Sopenharmony_ci elif path == '.': 2551cb0ef41Sopenharmony_ci path = '' 2561cb0ef41Sopenharmony_ci offset = len(path) 2571cb0ef41Sopenharmony_ci results = [] 2581cb0ef41Sopenharmony_ci def _walk(path): 2591cb0ef41Sopenharmony_ci for filename in self.provider.resource_listdir(path): 2601cb0ef41Sopenharmony_ci fullname = path + '/' + filename 2611cb0ef41Sopenharmony_ci if self.provider.resource_isdir(fullname): 2621cb0ef41Sopenharmony_ci _walk(fullname) 2631cb0ef41Sopenharmony_ci else: 2641cb0ef41Sopenharmony_ci results.append(fullname[offset:].lstrip('/')) 2651cb0ef41Sopenharmony_ci _walk(path) 2661cb0ef41Sopenharmony_ci results.sort() 2671cb0ef41Sopenharmony_ci return results 2681cb0ef41Sopenharmony_ci 2691cb0ef41Sopenharmony_ci 2701cb0ef41Sopenharmony_ciclass DictLoader(BaseLoader): 2711cb0ef41Sopenharmony_ci """Loads a template from a python dict. It's passed a dict of unicode 2721cb0ef41Sopenharmony_ci strings bound to template names. This loader is useful for unittesting: 2731cb0ef41Sopenharmony_ci 2741cb0ef41Sopenharmony_ci >>> loader = DictLoader({'index.html': 'source here'}) 2751cb0ef41Sopenharmony_ci 2761cb0ef41Sopenharmony_ci Because auto reloading is rarely useful this is disabled per default. 2771cb0ef41Sopenharmony_ci """ 2781cb0ef41Sopenharmony_ci 2791cb0ef41Sopenharmony_ci def __init__(self, mapping): 2801cb0ef41Sopenharmony_ci self.mapping = mapping 2811cb0ef41Sopenharmony_ci 2821cb0ef41Sopenharmony_ci def get_source(self, environment, template): 2831cb0ef41Sopenharmony_ci if template in self.mapping: 2841cb0ef41Sopenharmony_ci source = self.mapping[template] 2851cb0ef41Sopenharmony_ci return source, None, lambda: source == self.mapping.get(template) 2861cb0ef41Sopenharmony_ci raise TemplateNotFound(template) 2871cb0ef41Sopenharmony_ci 2881cb0ef41Sopenharmony_ci def list_templates(self): 2891cb0ef41Sopenharmony_ci return sorted(self.mapping) 2901cb0ef41Sopenharmony_ci 2911cb0ef41Sopenharmony_ci 2921cb0ef41Sopenharmony_ciclass FunctionLoader(BaseLoader): 2931cb0ef41Sopenharmony_ci """A loader that is passed a function which does the loading. The 2941cb0ef41Sopenharmony_ci function receives the name of the template and has to return either 2951cb0ef41Sopenharmony_ci an unicode string with the template source, a tuple in the form ``(source, 2961cb0ef41Sopenharmony_ci filename, uptodatefunc)`` or `None` if the template does not exist. 2971cb0ef41Sopenharmony_ci 2981cb0ef41Sopenharmony_ci >>> def load_template(name): 2991cb0ef41Sopenharmony_ci ... if name == 'index.html': 3001cb0ef41Sopenharmony_ci ... return '...' 3011cb0ef41Sopenharmony_ci ... 3021cb0ef41Sopenharmony_ci >>> loader = FunctionLoader(load_template) 3031cb0ef41Sopenharmony_ci 3041cb0ef41Sopenharmony_ci The `uptodatefunc` is a function that is called if autoreload is enabled 3051cb0ef41Sopenharmony_ci and has to return `True` if the template is still up to date. For more 3061cb0ef41Sopenharmony_ci details have a look at :meth:`BaseLoader.get_source` which has the same 3071cb0ef41Sopenharmony_ci return value. 3081cb0ef41Sopenharmony_ci """ 3091cb0ef41Sopenharmony_ci 3101cb0ef41Sopenharmony_ci def __init__(self, load_func): 3111cb0ef41Sopenharmony_ci self.load_func = load_func 3121cb0ef41Sopenharmony_ci 3131cb0ef41Sopenharmony_ci def get_source(self, environment, template): 3141cb0ef41Sopenharmony_ci rv = self.load_func(template) 3151cb0ef41Sopenharmony_ci if rv is None: 3161cb0ef41Sopenharmony_ci raise TemplateNotFound(template) 3171cb0ef41Sopenharmony_ci elif isinstance(rv, string_types): 3181cb0ef41Sopenharmony_ci return rv, None, None 3191cb0ef41Sopenharmony_ci return rv 3201cb0ef41Sopenharmony_ci 3211cb0ef41Sopenharmony_ci 3221cb0ef41Sopenharmony_ciclass PrefixLoader(BaseLoader): 3231cb0ef41Sopenharmony_ci """A loader that is passed a dict of loaders where each loader is bound 3241cb0ef41Sopenharmony_ci to a prefix. The prefix is delimited from the template by a slash per 3251cb0ef41Sopenharmony_ci default, which can be changed by setting the `delimiter` argument to 3261cb0ef41Sopenharmony_ci something else:: 3271cb0ef41Sopenharmony_ci 3281cb0ef41Sopenharmony_ci loader = PrefixLoader({ 3291cb0ef41Sopenharmony_ci 'app1': PackageLoader('mypackage.app1'), 3301cb0ef41Sopenharmony_ci 'app2': PackageLoader('mypackage.app2') 3311cb0ef41Sopenharmony_ci }) 3321cb0ef41Sopenharmony_ci 3331cb0ef41Sopenharmony_ci By loading ``'app1/index.html'`` the file from the app1 package is loaded, 3341cb0ef41Sopenharmony_ci by loading ``'app2/index.html'`` the file from the second. 3351cb0ef41Sopenharmony_ci """ 3361cb0ef41Sopenharmony_ci 3371cb0ef41Sopenharmony_ci def __init__(self, mapping, delimiter='/'): 3381cb0ef41Sopenharmony_ci self.mapping = mapping 3391cb0ef41Sopenharmony_ci self.delimiter = delimiter 3401cb0ef41Sopenharmony_ci 3411cb0ef41Sopenharmony_ci def get_loader(self, template): 3421cb0ef41Sopenharmony_ci try: 3431cb0ef41Sopenharmony_ci prefix, name = template.split(self.delimiter, 1) 3441cb0ef41Sopenharmony_ci loader = self.mapping[prefix] 3451cb0ef41Sopenharmony_ci except (ValueError, KeyError): 3461cb0ef41Sopenharmony_ci raise TemplateNotFound(template) 3471cb0ef41Sopenharmony_ci return loader, name 3481cb0ef41Sopenharmony_ci 3491cb0ef41Sopenharmony_ci def get_source(self, environment, template): 3501cb0ef41Sopenharmony_ci loader, name = self.get_loader(template) 3511cb0ef41Sopenharmony_ci try: 3521cb0ef41Sopenharmony_ci return loader.get_source(environment, name) 3531cb0ef41Sopenharmony_ci except TemplateNotFound: 3541cb0ef41Sopenharmony_ci # re-raise the exception with the correct filename here. 3551cb0ef41Sopenharmony_ci # (the one that includes the prefix) 3561cb0ef41Sopenharmony_ci raise TemplateNotFound(template) 3571cb0ef41Sopenharmony_ci 3581cb0ef41Sopenharmony_ci @internalcode 3591cb0ef41Sopenharmony_ci def load(self, environment, name, globals=None): 3601cb0ef41Sopenharmony_ci loader, local_name = self.get_loader(name) 3611cb0ef41Sopenharmony_ci try: 3621cb0ef41Sopenharmony_ci return loader.load(environment, local_name, globals) 3631cb0ef41Sopenharmony_ci except TemplateNotFound: 3641cb0ef41Sopenharmony_ci # re-raise the exception with the correct filename here. 3651cb0ef41Sopenharmony_ci # (the one that includes the prefix) 3661cb0ef41Sopenharmony_ci raise TemplateNotFound(name) 3671cb0ef41Sopenharmony_ci 3681cb0ef41Sopenharmony_ci def list_templates(self): 3691cb0ef41Sopenharmony_ci result = [] 3701cb0ef41Sopenharmony_ci for prefix, loader in iteritems(self.mapping): 3711cb0ef41Sopenharmony_ci for template in loader.list_templates(): 3721cb0ef41Sopenharmony_ci result.append(prefix + self.delimiter + template) 3731cb0ef41Sopenharmony_ci return result 3741cb0ef41Sopenharmony_ci 3751cb0ef41Sopenharmony_ci 3761cb0ef41Sopenharmony_ciclass ChoiceLoader(BaseLoader): 3771cb0ef41Sopenharmony_ci """This loader works like the `PrefixLoader` just that no prefix is 3781cb0ef41Sopenharmony_ci specified. If a template could not be found by one loader the next one 3791cb0ef41Sopenharmony_ci is tried. 3801cb0ef41Sopenharmony_ci 3811cb0ef41Sopenharmony_ci >>> loader = ChoiceLoader([ 3821cb0ef41Sopenharmony_ci ... FileSystemLoader('/path/to/user/templates'), 3831cb0ef41Sopenharmony_ci ... FileSystemLoader('/path/to/system/templates') 3841cb0ef41Sopenharmony_ci ... ]) 3851cb0ef41Sopenharmony_ci 3861cb0ef41Sopenharmony_ci This is useful if you want to allow users to override builtin templates 3871cb0ef41Sopenharmony_ci from a different location. 3881cb0ef41Sopenharmony_ci """ 3891cb0ef41Sopenharmony_ci 3901cb0ef41Sopenharmony_ci def __init__(self, loaders): 3911cb0ef41Sopenharmony_ci self.loaders = loaders 3921cb0ef41Sopenharmony_ci 3931cb0ef41Sopenharmony_ci def get_source(self, environment, template): 3941cb0ef41Sopenharmony_ci for loader in self.loaders: 3951cb0ef41Sopenharmony_ci try: 3961cb0ef41Sopenharmony_ci return loader.get_source(environment, template) 3971cb0ef41Sopenharmony_ci except TemplateNotFound: 3981cb0ef41Sopenharmony_ci pass 3991cb0ef41Sopenharmony_ci raise TemplateNotFound(template) 4001cb0ef41Sopenharmony_ci 4011cb0ef41Sopenharmony_ci @internalcode 4021cb0ef41Sopenharmony_ci def load(self, environment, name, globals=None): 4031cb0ef41Sopenharmony_ci for loader in self.loaders: 4041cb0ef41Sopenharmony_ci try: 4051cb0ef41Sopenharmony_ci return loader.load(environment, name, globals) 4061cb0ef41Sopenharmony_ci except TemplateNotFound: 4071cb0ef41Sopenharmony_ci pass 4081cb0ef41Sopenharmony_ci raise TemplateNotFound(name) 4091cb0ef41Sopenharmony_ci 4101cb0ef41Sopenharmony_ci def list_templates(self): 4111cb0ef41Sopenharmony_ci found = set() 4121cb0ef41Sopenharmony_ci for loader in self.loaders: 4131cb0ef41Sopenharmony_ci found.update(loader.list_templates()) 4141cb0ef41Sopenharmony_ci return sorted(found) 4151cb0ef41Sopenharmony_ci 4161cb0ef41Sopenharmony_ci 4171cb0ef41Sopenharmony_ciclass _TemplateModule(ModuleType): 4181cb0ef41Sopenharmony_ci """Like a normal module but with support for weak references""" 4191cb0ef41Sopenharmony_ci 4201cb0ef41Sopenharmony_ci 4211cb0ef41Sopenharmony_ciclass ModuleLoader(BaseLoader): 4221cb0ef41Sopenharmony_ci """This loader loads templates from precompiled templates. 4231cb0ef41Sopenharmony_ci 4241cb0ef41Sopenharmony_ci Example usage: 4251cb0ef41Sopenharmony_ci 4261cb0ef41Sopenharmony_ci >>> loader = ChoiceLoader([ 4271cb0ef41Sopenharmony_ci ... ModuleLoader('/path/to/compiled/templates'), 4281cb0ef41Sopenharmony_ci ... FileSystemLoader('/path/to/templates') 4291cb0ef41Sopenharmony_ci ... ]) 4301cb0ef41Sopenharmony_ci 4311cb0ef41Sopenharmony_ci Templates can be precompiled with :meth:`Environment.compile_templates`. 4321cb0ef41Sopenharmony_ci """ 4331cb0ef41Sopenharmony_ci 4341cb0ef41Sopenharmony_ci has_source_access = False 4351cb0ef41Sopenharmony_ci 4361cb0ef41Sopenharmony_ci def __init__(self, path): 4371cb0ef41Sopenharmony_ci package_name = '_jinja2_module_templates_%x' % id(self) 4381cb0ef41Sopenharmony_ci 4391cb0ef41Sopenharmony_ci # create a fake module that looks for the templates in the 4401cb0ef41Sopenharmony_ci # path given. 4411cb0ef41Sopenharmony_ci mod = _TemplateModule(package_name) 4421cb0ef41Sopenharmony_ci if isinstance(path, string_types): 4431cb0ef41Sopenharmony_ci path = [path] 4441cb0ef41Sopenharmony_ci else: 4451cb0ef41Sopenharmony_ci path = list(path) 4461cb0ef41Sopenharmony_ci mod.__path__ = path 4471cb0ef41Sopenharmony_ci 4481cb0ef41Sopenharmony_ci sys.modules[package_name] = weakref.proxy(mod, 4491cb0ef41Sopenharmony_ci lambda x: sys.modules.pop(package_name, None)) 4501cb0ef41Sopenharmony_ci 4511cb0ef41Sopenharmony_ci # the only strong reference, the sys.modules entry is weak 4521cb0ef41Sopenharmony_ci # so that the garbage collector can remove it once the 4531cb0ef41Sopenharmony_ci # loader that created it goes out of business. 4541cb0ef41Sopenharmony_ci self.module = mod 4551cb0ef41Sopenharmony_ci self.package_name = package_name 4561cb0ef41Sopenharmony_ci 4571cb0ef41Sopenharmony_ci @staticmethod 4581cb0ef41Sopenharmony_ci def get_template_key(name): 4591cb0ef41Sopenharmony_ci return 'tmpl_' + sha1(name.encode('utf-8')).hexdigest() 4601cb0ef41Sopenharmony_ci 4611cb0ef41Sopenharmony_ci @staticmethod 4621cb0ef41Sopenharmony_ci def get_module_filename(name): 4631cb0ef41Sopenharmony_ci return ModuleLoader.get_template_key(name) + '.py' 4641cb0ef41Sopenharmony_ci 4651cb0ef41Sopenharmony_ci @internalcode 4661cb0ef41Sopenharmony_ci def load(self, environment, name, globals=None): 4671cb0ef41Sopenharmony_ci key = self.get_template_key(name) 4681cb0ef41Sopenharmony_ci module = '%s.%s' % (self.package_name, key) 4691cb0ef41Sopenharmony_ci mod = getattr(self.module, module, None) 4701cb0ef41Sopenharmony_ci if mod is None: 4711cb0ef41Sopenharmony_ci try: 4721cb0ef41Sopenharmony_ci mod = __import__(module, None, None, ['root']) 4731cb0ef41Sopenharmony_ci except ImportError: 4741cb0ef41Sopenharmony_ci raise TemplateNotFound(name) 4751cb0ef41Sopenharmony_ci 4761cb0ef41Sopenharmony_ci # remove the entry from sys.modules, we only want the attribute 4771cb0ef41Sopenharmony_ci # on the module object we have stored on the loader. 4781cb0ef41Sopenharmony_ci sys.modules.pop(module, None) 4791cb0ef41Sopenharmony_ci 4801cb0ef41Sopenharmony_ci return environment.template_class.from_module_dict( 4811cb0ef41Sopenharmony_ci environment, mod.__dict__, globals) 482