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