mirror of https://github.com/opencv/opencv.git
Open Source Computer Vision Library
https://opencv.org/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
471 lines
17 KiB
471 lines
17 KiB
# -*- coding: utf-8 -*- |
|
""" |
|
jinja2.loaders |
|
~~~~~~~~~~~~~~ |
|
|
|
Jinja loader classes. |
|
|
|
:copyright: (c) 2010 by the Jinja Team. |
|
:license: BSD, see LICENSE for more details. |
|
""" |
|
import os |
|
import sys |
|
import weakref |
|
from types import ModuleType |
|
from os import path |
|
from hashlib import sha1 |
|
from jinja2.exceptions import TemplateNotFound |
|
from jinja2.utils import open_if_exists, internalcode |
|
from jinja2._compat import string_types, iteritems |
|
|
|
|
|
def split_template_path(template): |
|
"""Split a path into segments and perform a sanity check. If it detects |
|
'..' in the path it will raise a `TemplateNotFound` error. |
|
""" |
|
pieces = [] |
|
for piece in template.split('/'): |
|
if path.sep in piece \ |
|
or (path.altsep and path.altsep in piece) or \ |
|
piece == path.pardir: |
|
raise TemplateNotFound(template) |
|
elif piece and piece != '.': |
|
pieces.append(piece) |
|
return pieces |
|
|
|
|
|
class BaseLoader(object): |
|
"""Baseclass for all loaders. Subclass this and override `get_source` to |
|
implement a custom loading mechanism. The environment provides a |
|
`get_template` method that calls the loader's `load` method to get the |
|
:class:`Template` object. |
|
|
|
A very basic example for a loader that looks up templates on the file |
|
system could look like this:: |
|
|
|
from jinja2 import BaseLoader, TemplateNotFound |
|
from os.path import join, exists, getmtime |
|
|
|
class MyLoader(BaseLoader): |
|
|
|
def __init__(self, path): |
|
self.path = path |
|
|
|
def get_source(self, environment, template): |
|
path = join(self.path, template) |
|
if not exists(path): |
|
raise TemplateNotFound(template) |
|
mtime = getmtime(path) |
|
with file(path) as f: |
|
source = f.read().decode('utf-8') |
|
return source, path, lambda: mtime == getmtime(path) |
|
""" |
|
|
|
#: if set to `False` it indicates that the loader cannot provide access |
|
#: to the source of templates. |
|
#: |
|
#: .. versionadded:: 2.4 |
|
has_source_access = True |
|
|
|
def get_source(self, environment, template): |
|
"""Get the template source, filename and reload helper for a template. |
|
It's passed the environment and template name and has to return a |
|
tuple in the form ``(source, filename, uptodate)`` or raise a |
|
`TemplateNotFound` error if it can't locate the template. |
|
|
|
The source part of the returned tuple must be the source of the |
|
template as unicode string or a ASCII bytestring. The filename should |
|
be the name of the file on the filesystem if it was loaded from there, |
|
otherwise `None`. The filename is used by python for the tracebacks |
|
if no loader extension is used. |
|
|
|
The last item in the tuple is the `uptodate` function. If auto |
|
reloading is enabled it's always called to check if the template |
|
changed. No arguments are passed so the function must store the |
|
old state somewhere (for example in a closure). If it returns `False` |
|
the template will be reloaded. |
|
""" |
|
if not self.has_source_access: |
|
raise RuntimeError('%s cannot provide access to the source' % |
|
self.__class__.__name__) |
|
raise TemplateNotFound(template) |
|
|
|
def list_templates(self): |
|
"""Iterates over all templates. If the loader does not support that |
|
it should raise a :exc:`TypeError` which is the default behavior. |
|
""" |
|
raise TypeError('this loader cannot iterate over all templates') |
|
|
|
@internalcode |
|
def load(self, environment, name, globals=None): |
|
"""Loads a template. This method looks up the template in the cache |
|
or loads one by calling :meth:`get_source`. Subclasses should not |
|
override this method as loaders working on collections of other |
|
loaders (such as :class:`PrefixLoader` or :class:`ChoiceLoader`) |
|
will not call this method but `get_source` directly. |
|
""" |
|
code = None |
|
if globals is None: |
|
globals = {} |
|
|
|
# first we try to get the source for this template together |
|
# with the filename and the uptodate function. |
|
source, filename, uptodate = self.get_source(environment, name) |
|
|
|
# try to load the code from the bytecode cache if there is a |
|
# bytecode cache configured. |
|
bcc = environment.bytecode_cache |
|
if bcc is not None: |
|
bucket = bcc.get_bucket(environment, name, filename, source) |
|
code = bucket.code |
|
|
|
# if we don't have code so far (not cached, no longer up to |
|
# date) etc. we compile the template |
|
if code is None: |
|
code = environment.compile(source, name, filename) |
|
|
|
# if the bytecode cache is available and the bucket doesn't |
|
# have a code so far, we give the bucket the new code and put |
|
# it back to the bytecode cache. |
|
if bcc is not None and bucket.code is None: |
|
bucket.code = code |
|
bcc.set_bucket(bucket) |
|
|
|
return environment.template_class.from_code(environment, code, |
|
globals, uptodate) |
|
|
|
|
|
class FileSystemLoader(BaseLoader): |
|
"""Loads templates from the file system. This loader can find templates |
|
in folders on the file system and is the preferred way to load them. |
|
|
|
The loader takes the path to the templates as string, or if multiple |
|
locations are wanted a list of them which is then looked up in the |
|
given order: |
|
|
|
>>> loader = FileSystemLoader('/path/to/templates') |
|
>>> loader = FileSystemLoader(['/path/to/templates', '/other/path']) |
|
|
|
Per default the template encoding is ``'utf-8'`` which can be changed |
|
by setting the `encoding` parameter to something else. |
|
""" |
|
|
|
def __init__(self, searchpath, encoding='utf-8'): |
|
if isinstance(searchpath, string_types): |
|
searchpath = [searchpath] |
|
self.searchpath = list(searchpath) |
|
self.encoding = encoding |
|
|
|
def get_source(self, environment, template): |
|
pieces = split_template_path(template) |
|
for searchpath in self.searchpath: |
|
filename = path.join(searchpath, *pieces) |
|
f = open_if_exists(filename) |
|
if f is None: |
|
continue |
|
try: |
|
contents = f.read().decode(self.encoding) |
|
finally: |
|
f.close() |
|
|
|
mtime = path.getmtime(filename) |
|
def uptodate(): |
|
try: |
|
return path.getmtime(filename) == mtime |
|
except OSError: |
|
return False |
|
return contents, filename, uptodate |
|
raise TemplateNotFound(template) |
|
|
|
def list_templates(self): |
|
found = set() |
|
for searchpath in self.searchpath: |
|
for dirpath, dirnames, filenames in os.walk(searchpath): |
|
for filename in filenames: |
|
template = os.path.join(dirpath, filename) \ |
|
[len(searchpath):].strip(os.path.sep) \ |
|
.replace(os.path.sep, '/') |
|
if template[:2] == './': |
|
template = template[2:] |
|
if template not in found: |
|
found.add(template) |
|
return sorted(found) |
|
|
|
|
|
class PackageLoader(BaseLoader): |
|
"""Load templates from python eggs or packages. It is constructed with |
|
the name of the python package and the path to the templates in that |
|
package:: |
|
|
|
loader = PackageLoader('mypackage', 'views') |
|
|
|
If the package path is not given, ``'templates'`` is assumed. |
|
|
|
Per default the template encoding is ``'utf-8'`` which can be changed |
|
by setting the `encoding` parameter to something else. Due to the nature |
|
of eggs it's only possible to reload templates if the package was loaded |
|
from the file system and not a zip file. |
|
""" |
|
|
|
def __init__(self, package_name, package_path='templates', |
|
encoding='utf-8'): |
|
from pkg_resources import DefaultProvider, ResourceManager, \ |
|
get_provider |
|
provider = get_provider(package_name) |
|
self.encoding = encoding |
|
self.manager = ResourceManager() |
|
self.filesystem_bound = isinstance(provider, DefaultProvider) |
|
self.provider = provider |
|
self.package_path = package_path |
|
|
|
def get_source(self, environment, template): |
|
pieces = split_template_path(template) |
|
p = '/'.join((self.package_path,) + tuple(pieces)) |
|
if not self.provider.has_resource(p): |
|
raise TemplateNotFound(template) |
|
|
|
filename = uptodate = None |
|
if self.filesystem_bound: |
|
filename = self.provider.get_resource_filename(self.manager, p) |
|
mtime = path.getmtime(filename) |
|
def uptodate(): |
|
try: |
|
return path.getmtime(filename) == mtime |
|
except OSError: |
|
return False |
|
|
|
source = self.provider.get_resource_string(self.manager, p) |
|
return source.decode(self.encoding), filename, uptodate |
|
|
|
def list_templates(self): |
|
path = self.package_path |
|
if path[:2] == './': |
|
path = path[2:] |
|
elif path == '.': |
|
path = '' |
|
offset = len(path) |
|
results = [] |
|
def _walk(path): |
|
for filename in self.provider.resource_listdir(path): |
|
fullname = path + '/' + filename |
|
if self.provider.resource_isdir(fullname): |
|
_walk(fullname) |
|
else: |
|
results.append(fullname[offset:].lstrip('/')) |
|
_walk(path) |
|
results.sort() |
|
return results |
|
|
|
|
|
class DictLoader(BaseLoader): |
|
"""Loads a template from a python dict. It's passed a dict of unicode |
|
strings bound to template names. This loader is useful for unittesting: |
|
|
|
>>> loader = DictLoader({'index.html': 'source here'}) |
|
|
|
Because auto reloading is rarely useful this is disabled per default. |
|
""" |
|
|
|
def __init__(self, mapping): |
|
self.mapping = mapping |
|
|
|
def get_source(self, environment, template): |
|
if template in self.mapping: |
|
source = self.mapping[template] |
|
return source, None, lambda: source == self.mapping.get(template) |
|
raise TemplateNotFound(template) |
|
|
|
def list_templates(self): |
|
return sorted(self.mapping) |
|
|
|
|
|
class FunctionLoader(BaseLoader): |
|
"""A loader that is passed a function which does the loading. The |
|
function becomes the name of the template passed and has to return either |
|
an unicode string with the template source, a tuple in the form ``(source, |
|
filename, uptodatefunc)`` or `None` if the template does not exist. |
|
|
|
>>> def load_template(name): |
|
... if name == 'index.html': |
|
... return '...' |
|
... |
|
>>> loader = FunctionLoader(load_template) |
|
|
|
The `uptodatefunc` is a function that is called if autoreload is enabled |
|
and has to return `True` if the template is still up to date. For more |
|
details have a look at :meth:`BaseLoader.get_source` which has the same |
|
return value. |
|
""" |
|
|
|
def __init__(self, load_func): |
|
self.load_func = load_func |
|
|
|
def get_source(self, environment, template): |
|
rv = self.load_func(template) |
|
if rv is None: |
|
raise TemplateNotFound(template) |
|
elif isinstance(rv, string_types): |
|
return rv, None, None |
|
return rv |
|
|
|
|
|
class PrefixLoader(BaseLoader): |
|
"""A loader that is passed a dict of loaders where each loader is bound |
|
to a prefix. The prefix is delimited from the template by a slash per |
|
default, which can be changed by setting the `delimiter` argument to |
|
something else:: |
|
|
|
loader = PrefixLoader({ |
|
'app1': PackageLoader('mypackage.app1'), |
|
'app2': PackageLoader('mypackage.app2') |
|
}) |
|
|
|
By loading ``'app1/index.html'`` the file from the app1 package is loaded, |
|
by loading ``'app2/index.html'`` the file from the second. |
|
""" |
|
|
|
def __init__(self, mapping, delimiter='/'): |
|
self.mapping = mapping |
|
self.delimiter = delimiter |
|
|
|
def get_loader(self, template): |
|
try: |
|
prefix, name = template.split(self.delimiter, 1) |
|
loader = self.mapping[prefix] |
|
except (ValueError, KeyError): |
|
raise TemplateNotFound(template) |
|
return loader, name |
|
|
|
def get_source(self, environment, template): |
|
loader, name = self.get_loader(template) |
|
try: |
|
return loader.get_source(environment, name) |
|
except TemplateNotFound: |
|
# re-raise the exception with the correct fileame here. |
|
# (the one that includes the prefix) |
|
raise TemplateNotFound(template) |
|
|
|
@internalcode |
|
def load(self, environment, name, globals=None): |
|
loader, local_name = self.get_loader(name) |
|
try: |
|
return loader.load(environment, local_name) |
|
except TemplateNotFound: |
|
# re-raise the exception with the correct fileame here. |
|
# (the one that includes the prefix) |
|
raise TemplateNotFound(name) |
|
|
|
def list_templates(self): |
|
result = [] |
|
for prefix, loader in iteritems(self.mapping): |
|
for template in loader.list_templates(): |
|
result.append(prefix + self.delimiter + template) |
|
return result |
|
|
|
|
|
class ChoiceLoader(BaseLoader): |
|
"""This loader works like the `PrefixLoader` just that no prefix is |
|
specified. If a template could not be found by one loader the next one |
|
is tried. |
|
|
|
>>> loader = ChoiceLoader([ |
|
... FileSystemLoader('/path/to/user/templates'), |
|
... FileSystemLoader('/path/to/system/templates') |
|
... ]) |
|
|
|
This is useful if you want to allow users to override builtin templates |
|
from a different location. |
|
""" |
|
|
|
def __init__(self, loaders): |
|
self.loaders = loaders |
|
|
|
def get_source(self, environment, template): |
|
for loader in self.loaders: |
|
try: |
|
return loader.get_source(environment, template) |
|
except TemplateNotFound: |
|
pass |
|
raise TemplateNotFound(template) |
|
|
|
@internalcode |
|
def load(self, environment, name, globals=None): |
|
for loader in self.loaders: |
|
try: |
|
return loader.load(environment, name, globals) |
|
except TemplateNotFound: |
|
pass |
|
raise TemplateNotFound(name) |
|
|
|
def list_templates(self): |
|
found = set() |
|
for loader in self.loaders: |
|
found.update(loader.list_templates()) |
|
return sorted(found) |
|
|
|
|
|
class _TemplateModule(ModuleType): |
|
"""Like a normal module but with support for weak references""" |
|
|
|
|
|
class ModuleLoader(BaseLoader): |
|
"""This loader loads templates from precompiled templates. |
|
|
|
Example usage: |
|
|
|
>>> loader = ChoiceLoader([ |
|
... ModuleLoader('/path/to/compiled/templates'), |
|
... FileSystemLoader('/path/to/templates') |
|
... ]) |
|
|
|
Templates can be precompiled with :meth:`Environment.compile_templates`. |
|
""" |
|
|
|
has_source_access = False |
|
|
|
def __init__(self, path): |
|
package_name = '_jinja2_module_templates_%x' % id(self) |
|
|
|
# create a fake module that looks for the templates in the |
|
# path given. |
|
mod = _TemplateModule(package_name) |
|
if isinstance(path, string_types): |
|
path = [path] |
|
else: |
|
path = list(path) |
|
mod.__path__ = path |
|
|
|
sys.modules[package_name] = weakref.proxy(mod, |
|
lambda x: sys.modules.pop(package_name, None)) |
|
|
|
# the only strong reference, the sys.modules entry is weak |
|
# so that the garbage collector can remove it once the |
|
# loader that created it goes out of business. |
|
self.module = mod |
|
self.package_name = package_name |
|
|
|
@staticmethod |
|
def get_template_key(name): |
|
return 'tmpl_' + sha1(name.encode('utf-8')).hexdigest() |
|
|
|
@staticmethod |
|
def get_module_filename(name): |
|
return ModuleLoader.get_template_key(name) + '.py' |
|
|
|
@internalcode |
|
def load(self, environment, name, globals=None): |
|
key = self.get_template_key(name) |
|
module = '%s.%s' % (self.package_name, key) |
|
mod = getattr(self.module, module, None) |
|
if mod is None: |
|
try: |
|
mod = __import__(module, None, None, ['root']) |
|
except ImportError: |
|
raise TemplateNotFound(name) |
|
|
|
# remove the entry from sys.modules, we only want the attribute |
|
# on the module object we have stored on the loader. |
|
sys.modules.pop(module, None) |
|
|
|
return environment.template_class.from_module_dict( |
|
environment, mod.__dict__, globals)
|
|
|