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.
1191 lines
46 KiB
1191 lines
46 KiB
# -*- coding: utf-8 -*- |
|
""" |
|
jinja2.environment |
|
~~~~~~~~~~~~~~~~~~ |
|
|
|
Provides a class that holds runtime and parsing time options. |
|
|
|
:copyright: (c) 2010 by the Jinja Team. |
|
:license: BSD, see LICENSE for more details. |
|
""" |
|
import os |
|
import sys |
|
from jinja2 import nodes |
|
from jinja2.defaults import BLOCK_START_STRING, \ |
|
BLOCK_END_STRING, VARIABLE_START_STRING, VARIABLE_END_STRING, \ |
|
COMMENT_START_STRING, COMMENT_END_STRING, LINE_STATEMENT_PREFIX, \ |
|
LINE_COMMENT_PREFIX, TRIM_BLOCKS, NEWLINE_SEQUENCE, \ |
|
DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE, \ |
|
KEEP_TRAILING_NEWLINE, LSTRIP_BLOCKS |
|
from jinja2.lexer import get_lexer, TokenStream |
|
from jinja2.parser import Parser |
|
from jinja2.nodes import EvalContext |
|
from jinja2.optimizer import optimize |
|
from jinja2.compiler import generate |
|
from jinja2.runtime import Undefined, new_context |
|
from jinja2.exceptions import TemplateSyntaxError, TemplateNotFound, \ |
|
TemplatesNotFound, TemplateRuntimeError |
|
from jinja2.utils import import_string, LRUCache, Markup, missing, \ |
|
concat, consume, internalcode |
|
from jinja2._compat import imap, ifilter, string_types, iteritems, \ |
|
text_type, reraise, implements_iterator, implements_to_string, \ |
|
get_next, encode_filename, PY2, PYPY |
|
from functools import reduce |
|
|
|
|
|
# for direct template usage we have up to ten living environments |
|
_spontaneous_environments = LRUCache(10) |
|
|
|
# the function to create jinja traceback objects. This is dynamically |
|
# imported on the first exception in the exception handler. |
|
_make_traceback = None |
|
|
|
|
|
def get_spontaneous_environment(*args): |
|
"""Return a new spontaneous environment. A spontaneous environment is an |
|
unnamed and unaccessible (in theory) environment that is used for |
|
templates generated from a string and not from the file system. |
|
""" |
|
try: |
|
env = _spontaneous_environments.get(args) |
|
except TypeError: |
|
return Environment(*args) |
|
if env is not None: |
|
return env |
|
_spontaneous_environments[args] = env = Environment(*args) |
|
env.shared = True |
|
return env |
|
|
|
|
|
def create_cache(size): |
|
"""Return the cache class for the given size.""" |
|
if size == 0: |
|
return None |
|
if size < 0: |
|
return {} |
|
return LRUCache(size) |
|
|
|
|
|
def copy_cache(cache): |
|
"""Create an empty copy of the given cache.""" |
|
if cache is None: |
|
return None |
|
elif type(cache) is dict: |
|
return {} |
|
return LRUCache(cache.capacity) |
|
|
|
|
|
def load_extensions(environment, extensions): |
|
"""Load the extensions from the list and bind it to the environment. |
|
Returns a dict of instantiated environments. |
|
""" |
|
result = {} |
|
for extension in extensions: |
|
if isinstance(extension, string_types): |
|
extension = import_string(extension) |
|
result[extension.identifier] = extension(environment) |
|
return result |
|
|
|
|
|
def _environment_sanity_check(environment): |
|
"""Perform a sanity check on the environment.""" |
|
assert issubclass(environment.undefined, Undefined), 'undefined must ' \ |
|
'be a subclass of undefined because filters depend on it.' |
|
assert environment.block_start_string != \ |
|
environment.variable_start_string != \ |
|
environment.comment_start_string, 'block, variable and comment ' \ |
|
'start strings must be different' |
|
assert environment.newline_sequence in ('\r', '\r\n', '\n'), \ |
|
'newline_sequence set to unknown line ending string.' |
|
return environment |
|
|
|
|
|
class Environment(object): |
|
r"""The core component of Jinja is the `Environment`. It contains |
|
important shared variables like configuration, filters, tests, |
|
globals and others. Instances of this class may be modified if |
|
they are not shared and if no template was loaded so far. |
|
Modifications on environments after the first template was loaded |
|
will lead to surprising effects and undefined behavior. |
|
|
|
Here the possible initialization parameters: |
|
|
|
`block_start_string` |
|
The string marking the begin of a block. Defaults to ``'{%'``. |
|
|
|
`block_end_string` |
|
The string marking the end of a block. Defaults to ``'%}'``. |
|
|
|
`variable_start_string` |
|
The string marking the begin of a print statement. |
|
Defaults to ``'{{'``. |
|
|
|
`variable_end_string` |
|
The string marking the end of a print statement. Defaults to |
|
``'}}'``. |
|
|
|
`comment_start_string` |
|
The string marking the begin of a comment. Defaults to ``'{#'``. |
|
|
|
`comment_end_string` |
|
The string marking the end of a comment. Defaults to ``'#}'``. |
|
|
|
`line_statement_prefix` |
|
If given and a string, this will be used as prefix for line based |
|
statements. See also :ref:`line-statements`. |
|
|
|
`line_comment_prefix` |
|
If given and a string, this will be used as prefix for line based |
|
based comments. See also :ref:`line-statements`. |
|
|
|
.. versionadded:: 2.2 |
|
|
|
`trim_blocks` |
|
If this is set to ``True`` the first newline after a block is |
|
removed (block, not variable tag!). Defaults to `False`. |
|
|
|
`lstrip_blocks` |
|
If this is set to ``True`` leading spaces and tabs are stripped |
|
from the start of a line to a block. Defaults to `False`. |
|
|
|
`newline_sequence` |
|
The sequence that starts a newline. Must be one of ``'\r'``, |
|
``'\n'`` or ``'\r\n'``. The default is ``'\n'`` which is a |
|
useful default for Linux and OS X systems as well as web |
|
applications. |
|
|
|
`keep_trailing_newline` |
|
Preserve the trailing newline when rendering templates. |
|
The default is ``False``, which causes a single newline, |
|
if present, to be stripped from the end of the template. |
|
|
|
.. versionadded:: 2.7 |
|
|
|
`extensions` |
|
List of Jinja extensions to use. This can either be import paths |
|
as strings or extension classes. For more information have a |
|
look at :ref:`the extensions documentation <jinja-extensions>`. |
|
|
|
`optimized` |
|
should the optimizer be enabled? Default is `True`. |
|
|
|
`undefined` |
|
:class:`Undefined` or a subclass of it that is used to represent |
|
undefined values in the template. |
|
|
|
`finalize` |
|
A callable that can be used to process the result of a variable |
|
expression before it is output. For example one can convert |
|
`None` implicitly into an empty string here. |
|
|
|
`autoescape` |
|
If set to true the XML/HTML autoescaping feature is enabled by |
|
default. For more details about auto escaping see |
|
:class:`~jinja2.utils.Markup`. As of Jinja 2.4 this can also |
|
be a callable that is passed the template name and has to |
|
return `True` or `False` depending on autoescape should be |
|
enabled by default. |
|
|
|
.. versionchanged:: 2.4 |
|
`autoescape` can now be a function |
|
|
|
`loader` |
|
The template loader for this environment. |
|
|
|
`cache_size` |
|
The size of the cache. Per default this is ``50`` which means |
|
that if more than 50 templates are loaded the loader will clean |
|
out the least recently used template. If the cache size is set to |
|
``0`` templates are recompiled all the time, if the cache size is |
|
``-1`` the cache will not be cleaned. |
|
|
|
`auto_reload` |
|
Some loaders load templates from locations where the template |
|
sources may change (ie: file system or database). If |
|
`auto_reload` is set to `True` (default) every time a template is |
|
requested the loader checks if the source changed and if yes, it |
|
will reload the template. For higher performance it's possible to |
|
disable that. |
|
|
|
`bytecode_cache` |
|
If set to a bytecode cache object, this object will provide a |
|
cache for the internal Jinja bytecode so that templates don't |
|
have to be parsed if they were not changed. |
|
|
|
See :ref:`bytecode-cache` for more information. |
|
""" |
|
|
|
#: if this environment is sandboxed. Modifying this variable won't make |
|
#: the environment sandboxed though. For a real sandboxed environment |
|
#: have a look at jinja2.sandbox. This flag alone controls the code |
|
#: generation by the compiler. |
|
sandboxed = False |
|
|
|
#: True if the environment is just an overlay |
|
overlayed = False |
|
|
|
#: the environment this environment is linked to if it is an overlay |
|
linked_to = None |
|
|
|
#: shared environments have this set to `True`. A shared environment |
|
#: must not be modified |
|
shared = False |
|
|
|
#: these are currently EXPERIMENTAL undocumented features. |
|
exception_handler = None |
|
exception_formatter = None |
|
|
|
def __init__(self, |
|
block_start_string=BLOCK_START_STRING, |
|
block_end_string=BLOCK_END_STRING, |
|
variable_start_string=VARIABLE_START_STRING, |
|
variable_end_string=VARIABLE_END_STRING, |
|
comment_start_string=COMMENT_START_STRING, |
|
comment_end_string=COMMENT_END_STRING, |
|
line_statement_prefix=LINE_STATEMENT_PREFIX, |
|
line_comment_prefix=LINE_COMMENT_PREFIX, |
|
trim_blocks=TRIM_BLOCKS, |
|
lstrip_blocks=LSTRIP_BLOCKS, |
|
newline_sequence=NEWLINE_SEQUENCE, |
|
keep_trailing_newline=KEEP_TRAILING_NEWLINE, |
|
extensions=(), |
|
optimized=True, |
|
undefined=Undefined, |
|
finalize=None, |
|
autoescape=False, |
|
loader=None, |
|
cache_size=50, |
|
auto_reload=True, |
|
bytecode_cache=None): |
|
# !!Important notice!! |
|
# The constructor accepts quite a few arguments that should be |
|
# passed by keyword rather than position. However it's important to |
|
# not change the order of arguments because it's used at least |
|
# internally in those cases: |
|
# - spontaneous environments (i18n extension and Template) |
|
# - unittests |
|
# If parameter changes are required only add parameters at the end |
|
# and don't change the arguments (or the defaults!) of the arguments |
|
# existing already. |
|
|
|
# lexer / parser information |
|
self.block_start_string = block_start_string |
|
self.block_end_string = block_end_string |
|
self.variable_start_string = variable_start_string |
|
self.variable_end_string = variable_end_string |
|
self.comment_start_string = comment_start_string |
|
self.comment_end_string = comment_end_string |
|
self.line_statement_prefix = line_statement_prefix |
|
self.line_comment_prefix = line_comment_prefix |
|
self.trim_blocks = trim_blocks |
|
self.lstrip_blocks = lstrip_blocks |
|
self.newline_sequence = newline_sequence |
|
self.keep_trailing_newline = keep_trailing_newline |
|
|
|
# runtime information |
|
self.undefined = undefined |
|
self.optimized = optimized |
|
self.finalize = finalize |
|
self.autoescape = autoescape |
|
|
|
# defaults |
|
self.filters = DEFAULT_FILTERS.copy() |
|
self.tests = DEFAULT_TESTS.copy() |
|
self.globals = DEFAULT_NAMESPACE.copy() |
|
|
|
# set the loader provided |
|
self.loader = loader |
|
self.cache = create_cache(cache_size) |
|
self.bytecode_cache = bytecode_cache |
|
self.auto_reload = auto_reload |
|
|
|
# load extensions |
|
self.extensions = load_extensions(self, extensions) |
|
|
|
_environment_sanity_check(self) |
|
|
|
def add_extension(self, extension): |
|
"""Adds an extension after the environment was created. |
|
|
|
.. versionadded:: 2.5 |
|
""" |
|
self.extensions.update(load_extensions(self, [extension])) |
|
|
|
def extend(self, **attributes): |
|
"""Add the items to the instance of the environment if they do not exist |
|
yet. This is used by :ref:`extensions <writing-extensions>` to register |
|
callbacks and configuration values without breaking inheritance. |
|
""" |
|
for key, value in iteritems(attributes): |
|
if not hasattr(self, key): |
|
setattr(self, key, value) |
|
|
|
def overlay(self, block_start_string=missing, block_end_string=missing, |
|
variable_start_string=missing, variable_end_string=missing, |
|
comment_start_string=missing, comment_end_string=missing, |
|
line_statement_prefix=missing, line_comment_prefix=missing, |
|
trim_blocks=missing, lstrip_blocks=missing, |
|
extensions=missing, optimized=missing, |
|
undefined=missing, finalize=missing, autoescape=missing, |
|
loader=missing, cache_size=missing, auto_reload=missing, |
|
bytecode_cache=missing): |
|
"""Create a new overlay environment that shares all the data with the |
|
current environment except of cache and the overridden attributes. |
|
Extensions cannot be removed for an overlayed environment. An overlayed |
|
environment automatically gets all the extensions of the environment it |
|
is linked to plus optional extra extensions. |
|
|
|
Creating overlays should happen after the initial environment was set |
|
up completely. Not all attributes are truly linked, some are just |
|
copied over so modifications on the original environment may not shine |
|
through. |
|
""" |
|
args = dict(locals()) |
|
del args['self'], args['cache_size'], args['extensions'] |
|
|
|
rv = object.__new__(self.__class__) |
|
rv.__dict__.update(self.__dict__) |
|
rv.overlayed = True |
|
rv.linked_to = self |
|
|
|
for key, value in iteritems(args): |
|
if value is not missing: |
|
setattr(rv, key, value) |
|
|
|
if cache_size is not missing: |
|
rv.cache = create_cache(cache_size) |
|
else: |
|
rv.cache = copy_cache(self.cache) |
|
|
|
rv.extensions = {} |
|
for key, value in iteritems(self.extensions): |
|
rv.extensions[key] = value.bind(rv) |
|
if extensions is not missing: |
|
rv.extensions.update(load_extensions(rv, extensions)) |
|
|
|
return _environment_sanity_check(rv) |
|
|
|
lexer = property(get_lexer, doc="The lexer for this environment.") |
|
|
|
def iter_extensions(self): |
|
"""Iterates over the extensions by priority.""" |
|
return iter(sorted(self.extensions.values(), |
|
key=lambda x: x.priority)) |
|
|
|
def getitem(self, obj, argument): |
|
"""Get an item or attribute of an object but prefer the item.""" |
|
try: |
|
return obj[argument] |
|
except (TypeError, LookupError): |
|
if isinstance(argument, string_types): |
|
try: |
|
attr = str(argument) |
|
except Exception: |
|
pass |
|
else: |
|
try: |
|
return getattr(obj, attr) |
|
except AttributeError: |
|
pass |
|
return self.undefined(obj=obj, name=argument) |
|
|
|
def getattr(self, obj, attribute): |
|
"""Get an item or attribute of an object but prefer the attribute. |
|
Unlike :meth:`getitem` the attribute *must* be a bytestring. |
|
""" |
|
try: |
|
return getattr(obj, attribute) |
|
except AttributeError: |
|
pass |
|
try: |
|
return obj[attribute] |
|
except (TypeError, LookupError, AttributeError): |
|
return self.undefined(obj=obj, name=attribute) |
|
|
|
def call_filter(self, name, value, args=None, kwargs=None, |
|
context=None, eval_ctx=None): |
|
"""Invokes a filter on a value the same way the compiler does it. |
|
|
|
.. versionadded:: 2.7 |
|
""" |
|
func = self.filters.get(name) |
|
if func is None: |
|
raise TemplateRuntimeError('no filter named %r' % name) |
|
args = [value] + list(args or ()) |
|
if getattr(func, 'contextfilter', False): |
|
if context is None: |
|
raise TemplateRuntimeError('Attempted to invoke context ' |
|
'filter without context') |
|
args.insert(0, context) |
|
elif getattr(func, 'evalcontextfilter', False): |
|
if eval_ctx is None: |
|
if context is not None: |
|
eval_ctx = context.eval_ctx |
|
else: |
|
eval_ctx = EvalContext(self) |
|
args.insert(0, eval_ctx) |
|
elif getattr(func, 'environmentfilter', False): |
|
args.insert(0, self) |
|
return func(*args, **(kwargs or {})) |
|
|
|
def call_test(self, name, value, args=None, kwargs=None): |
|
"""Invokes a test on a value the same way the compiler does it. |
|
|
|
.. versionadded:: 2.7 |
|
""" |
|
func = self.tests.get(name) |
|
if func is None: |
|
raise TemplateRuntimeError('no test named %r' % name) |
|
return func(value, *(args or ()), **(kwargs or {})) |
|
|
|
@internalcode |
|
def parse(self, source, name=None, filename=None): |
|
"""Parse the sourcecode and return the abstract syntax tree. This |
|
tree of nodes is used by the compiler to convert the template into |
|
executable source- or bytecode. This is useful for debugging or to |
|
extract information from templates. |
|
|
|
If you are :ref:`developing Jinja2 extensions <writing-extensions>` |
|
this gives you a good overview of the node tree generated. |
|
""" |
|
try: |
|
return self._parse(source, name, filename) |
|
except TemplateSyntaxError: |
|
exc_info = sys.exc_info() |
|
self.handle_exception(exc_info, source_hint=source) |
|
|
|
def _parse(self, source, name, filename): |
|
"""Internal parsing function used by `parse` and `compile`.""" |
|
return Parser(self, source, name, encode_filename(filename)).parse() |
|
|
|
def lex(self, source, name=None, filename=None): |
|
"""Lex the given sourcecode and return a generator that yields |
|
tokens as tuples in the form ``(lineno, token_type, value)``. |
|
This can be useful for :ref:`extension development <writing-extensions>` |
|
and debugging templates. |
|
|
|
This does not perform preprocessing. If you want the preprocessing |
|
of the extensions to be applied you have to filter source through |
|
the :meth:`preprocess` method. |
|
""" |
|
source = text_type(source) |
|
try: |
|
return self.lexer.tokeniter(source, name, filename) |
|
except TemplateSyntaxError: |
|
exc_info = sys.exc_info() |
|
self.handle_exception(exc_info, source_hint=source) |
|
|
|
def preprocess(self, source, name=None, filename=None): |
|
"""Preprocesses the source with all extensions. This is automatically |
|
called for all parsing and compiling methods but *not* for :meth:`lex` |
|
because there you usually only want the actual source tokenized. |
|
""" |
|
return reduce(lambda s, e: e.preprocess(s, name, filename), |
|
self.iter_extensions(), text_type(source)) |
|
|
|
def _tokenize(self, source, name, filename=None, state=None): |
|
"""Called by the parser to do the preprocessing and filtering |
|
for all the extensions. Returns a :class:`~jinja2.lexer.TokenStream`. |
|
""" |
|
source = self.preprocess(source, name, filename) |
|
stream = self.lexer.tokenize(source, name, filename, state) |
|
for ext in self.iter_extensions(): |
|
stream = ext.filter_stream(stream) |
|
if not isinstance(stream, TokenStream): |
|
stream = TokenStream(stream, name, filename) |
|
return stream |
|
|
|
def _generate(self, source, name, filename, defer_init=False): |
|
"""Internal hook that can be overridden to hook a different generate |
|
method in. |
|
|
|
.. versionadded:: 2.5 |
|
""" |
|
return generate(source, self, name, filename, defer_init=defer_init) |
|
|
|
def _compile(self, source, filename): |
|
"""Internal hook that can be overridden to hook a different compile |
|
method in. |
|
|
|
.. versionadded:: 2.5 |
|
""" |
|
return compile(source, filename, 'exec') |
|
|
|
@internalcode |
|
def compile(self, source, name=None, filename=None, raw=False, |
|
defer_init=False): |
|
"""Compile a node or template source code. The `name` parameter is |
|
the load name of the template after it was joined using |
|
:meth:`join_path` if necessary, not the filename on the file system. |
|
the `filename` parameter is the estimated filename of the template on |
|
the file system. If the template came from a database or memory this |
|
can be omitted. |
|
|
|
The return value of this method is a python code object. If the `raw` |
|
parameter is `True` the return value will be a string with python |
|
code equivalent to the bytecode returned otherwise. This method is |
|
mainly used internally. |
|
|
|
`defer_init` is use internally to aid the module code generator. This |
|
causes the generated code to be able to import without the global |
|
environment variable to be set. |
|
|
|
.. versionadded:: 2.4 |
|
`defer_init` parameter added. |
|
""" |
|
source_hint = None |
|
try: |
|
if isinstance(source, string_types): |
|
source_hint = source |
|
source = self._parse(source, name, filename) |
|
if self.optimized: |
|
source = optimize(source, self) |
|
source = self._generate(source, name, filename, |
|
defer_init=defer_init) |
|
if raw: |
|
return source |
|
if filename is None: |
|
filename = '<template>' |
|
else: |
|
filename = encode_filename(filename) |
|
return self._compile(source, filename) |
|
except TemplateSyntaxError: |
|
exc_info = sys.exc_info() |
|
self.handle_exception(exc_info, source_hint=source) |
|
|
|
def compile_expression(self, source, undefined_to_none=True): |
|
"""A handy helper method that returns a callable that accepts keyword |
|
arguments that appear as variables in the expression. If called it |
|
returns the result of the expression. |
|
|
|
This is useful if applications want to use the same rules as Jinja |
|
in template "configuration files" or similar situations. |
|
|
|
Example usage: |
|
|
|
>>> env = Environment() |
|
>>> expr = env.compile_expression('foo == 42') |
|
>>> expr(foo=23) |
|
False |
|
>>> expr(foo=42) |
|
True |
|
|
|
Per default the return value is converted to `None` if the |
|
expression returns an undefined value. This can be changed |
|
by setting `undefined_to_none` to `False`. |
|
|
|
>>> env.compile_expression('var')() is None |
|
True |
|
>>> env.compile_expression('var', undefined_to_none=False)() |
|
Undefined |
|
|
|
.. versionadded:: 2.1 |
|
""" |
|
parser = Parser(self, source, state='variable') |
|
exc_info = None |
|
try: |
|
expr = parser.parse_expression() |
|
if not parser.stream.eos: |
|
raise TemplateSyntaxError('chunk after expression', |
|
parser.stream.current.lineno, |
|
None, None) |
|
expr.set_environment(self) |
|
except TemplateSyntaxError: |
|
exc_info = sys.exc_info() |
|
if exc_info is not None: |
|
self.handle_exception(exc_info, source_hint=source) |
|
body = [nodes.Assign(nodes.Name('result', 'store'), expr, lineno=1)] |
|
template = self.from_string(nodes.Template(body, lineno=1)) |
|
return TemplateExpression(template, undefined_to_none) |
|
|
|
def compile_templates(self, target, extensions=None, filter_func=None, |
|
zip='deflated', log_function=None, |
|
ignore_errors=True, py_compile=False): |
|
"""Finds all the templates the loader can find, compiles them |
|
and stores them in `target`. If `zip` is `None`, instead of in a |
|
zipfile, the templates will be will be stored in a directory. |
|
By default a deflate zip algorithm is used, to switch to |
|
the stored algorithm, `zip` can be set to ``'stored'``. |
|
|
|
`extensions` and `filter_func` are passed to :meth:`list_templates`. |
|
Each template returned will be compiled to the target folder or |
|
zipfile. |
|
|
|
By default template compilation errors are ignored. In case a |
|
log function is provided, errors are logged. If you want template |
|
syntax errors to abort the compilation you can set `ignore_errors` |
|
to `False` and you will get an exception on syntax errors. |
|
|
|
If `py_compile` is set to `True` .pyc files will be written to the |
|
target instead of standard .py files. This flag does not do anything |
|
on pypy and Python 3 where pyc files are not picked up by itself and |
|
don't give much benefit. |
|
|
|
.. versionadded:: 2.4 |
|
""" |
|
from jinja2.loaders import ModuleLoader |
|
|
|
if log_function is None: |
|
log_function = lambda x: None |
|
|
|
if py_compile: |
|
if not PY2 or PYPY: |
|
from warnings import warn |
|
warn(Warning('py_compile has no effect on pypy or Python 3')) |
|
py_compile = False |
|
else: |
|
import imp, marshal |
|
py_header = imp.get_magic() + \ |
|
u'\xff\xff\xff\xff'.encode('iso-8859-15') |
|
|
|
# Python 3.3 added a source filesize to the header |
|
if sys.version_info >= (3, 3): |
|
py_header += u'\x00\x00\x00\x00'.encode('iso-8859-15') |
|
|
|
def write_file(filename, data, mode): |
|
if zip: |
|
info = ZipInfo(filename) |
|
info.external_attr = 0o755 << 16 |
|
zip_file.writestr(info, data) |
|
else: |
|
f = open(os.path.join(target, filename), mode) |
|
try: |
|
f.write(data) |
|
finally: |
|
f.close() |
|
|
|
if zip is not None: |
|
from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED, ZIP_STORED |
|
zip_file = ZipFile(target, 'w', dict(deflated=ZIP_DEFLATED, |
|
stored=ZIP_STORED)[zip]) |
|
log_function('Compiling into Zip archive "%s"' % target) |
|
else: |
|
if not os.path.isdir(target): |
|
os.makedirs(target) |
|
log_function('Compiling into folder "%s"' % target) |
|
|
|
try: |
|
for name in self.list_templates(extensions, filter_func): |
|
source, filename, _ = self.loader.get_source(self, name) |
|
try: |
|
code = self.compile(source, name, filename, True, True) |
|
except TemplateSyntaxError as e: |
|
if not ignore_errors: |
|
raise |
|
log_function('Could not compile "%s": %s' % (name, e)) |
|
continue |
|
|
|
filename = ModuleLoader.get_module_filename(name) |
|
|
|
if py_compile: |
|
c = self._compile(code, encode_filename(filename)) |
|
write_file(filename + 'c', py_header + |
|
marshal.dumps(c), 'wb') |
|
log_function('Byte-compiled "%s" as %s' % |
|
(name, filename + 'c')) |
|
else: |
|
write_file(filename, code, 'w') |
|
log_function('Compiled "%s" as %s' % (name, filename)) |
|
finally: |
|
if zip: |
|
zip_file.close() |
|
|
|
log_function('Finished compiling templates') |
|
|
|
def list_templates(self, extensions=None, filter_func=None): |
|
"""Returns a list of templates for this environment. This requires |
|
that the loader supports the loader's |
|
:meth:`~BaseLoader.list_templates` method. |
|
|
|
If there are other files in the template folder besides the |
|
actual templates, the returned list can be filtered. There are two |
|
ways: either `extensions` is set to a list of file extensions for |
|
templates, or a `filter_func` can be provided which is a callable that |
|
is passed a template name and should return `True` if it should end up |
|
in the result list. |
|
|
|
If the loader does not support that, a :exc:`TypeError` is raised. |
|
|
|
.. versionadded:: 2.4 |
|
""" |
|
x = self.loader.list_templates() |
|
if extensions is not None: |
|
if filter_func is not None: |
|
raise TypeError('either extensions or filter_func ' |
|
'can be passed, but not both') |
|
filter_func = lambda x: '.' in x and \ |
|
x.rsplit('.', 1)[1] in extensions |
|
if filter_func is not None: |
|
x = ifilter(filter_func, x) |
|
return x |
|
|
|
def handle_exception(self, exc_info=None, rendered=False, source_hint=None): |
|
"""Exception handling helper. This is used internally to either raise |
|
rewritten exceptions or return a rendered traceback for the template. |
|
""" |
|
global _make_traceback |
|
if exc_info is None: |
|
exc_info = sys.exc_info() |
|
|
|
# the debugging module is imported when it's used for the first time. |
|
# we're doing a lot of stuff there and for applications that do not |
|
# get any exceptions in template rendering there is no need to load |
|
# all of that. |
|
if _make_traceback is None: |
|
from jinja2.debug import make_traceback as _make_traceback |
|
traceback = _make_traceback(exc_info, source_hint) |
|
if rendered and self.exception_formatter is not None: |
|
return self.exception_formatter(traceback) |
|
if self.exception_handler is not None: |
|
self.exception_handler(traceback) |
|
exc_type, exc_value, tb = traceback.standard_exc_info |
|
reraise(exc_type, exc_value, tb) |
|
|
|
def join_path(self, template, parent): |
|
"""Join a template with the parent. By default all the lookups are |
|
relative to the loader root so this method returns the `template` |
|
parameter unchanged, but if the paths should be relative to the |
|
parent template, this function can be used to calculate the real |
|
template name. |
|
|
|
Subclasses may override this method and implement template path |
|
joining here. |
|
""" |
|
return template |
|
|
|
@internalcode |
|
def _load_template(self, name, globals): |
|
if self.loader is None: |
|
raise TypeError('no loader for this environment specified') |
|
if self.cache is not None: |
|
template = self.cache.get(name) |
|
if template is not None and (not self.auto_reload or \ |
|
template.is_up_to_date): |
|
return template |
|
template = self.loader.load(self, name, globals) |
|
if self.cache is not None: |
|
self.cache[name] = template |
|
return template |
|
|
|
@internalcode |
|
def get_template(self, name, parent=None, globals=None): |
|
"""Load a template from the loader. If a loader is configured this |
|
method ask the loader for the template and returns a :class:`Template`. |
|
If the `parent` parameter is not `None`, :meth:`join_path` is called |
|
to get the real template name before loading. |
|
|
|
The `globals` parameter can be used to provide template wide globals. |
|
These variables are available in the context at render time. |
|
|
|
If the template does not exist a :exc:`TemplateNotFound` exception is |
|
raised. |
|
|
|
.. versionchanged:: 2.4 |
|
If `name` is a :class:`Template` object it is returned from the |
|
function unchanged. |
|
""" |
|
if isinstance(name, Template): |
|
return name |
|
if parent is not None: |
|
name = self.join_path(name, parent) |
|
return self._load_template(name, self.make_globals(globals)) |
|
|
|
@internalcode |
|
def select_template(self, names, parent=None, globals=None): |
|
"""Works like :meth:`get_template` but tries a number of templates |
|
before it fails. If it cannot find any of the templates, it will |
|
raise a :exc:`TemplatesNotFound` exception. |
|
|
|
.. versionadded:: 2.3 |
|
|
|
.. versionchanged:: 2.4 |
|
If `names` contains a :class:`Template` object it is returned |
|
from the function unchanged. |
|
""" |
|
if not names: |
|
raise TemplatesNotFound(message=u'Tried to select from an empty list ' |
|
u'of templates.') |
|
globals = self.make_globals(globals) |
|
for name in names: |
|
if isinstance(name, Template): |
|
return name |
|
if parent is not None: |
|
name = self.join_path(name, parent) |
|
try: |
|
return self._load_template(name, globals) |
|
except TemplateNotFound: |
|
pass |
|
raise TemplatesNotFound(names) |
|
|
|
@internalcode |
|
def get_or_select_template(self, template_name_or_list, |
|
parent=None, globals=None): |
|
"""Does a typecheck and dispatches to :meth:`select_template` |
|
if an iterable of template names is given, otherwise to |
|
:meth:`get_template`. |
|
|
|
.. versionadded:: 2.3 |
|
""" |
|
if isinstance(template_name_or_list, string_types): |
|
return self.get_template(template_name_or_list, parent, globals) |
|
elif isinstance(template_name_or_list, Template): |
|
return template_name_or_list |
|
return self.select_template(template_name_or_list, parent, globals) |
|
|
|
def from_string(self, source, globals=None, template_class=None): |
|
"""Load a template from a string. This parses the source given and |
|
returns a :class:`Template` object. |
|
""" |
|
globals = self.make_globals(globals) |
|
cls = template_class or self.template_class |
|
return cls.from_code(self, self.compile(source), globals, None) |
|
|
|
def make_globals(self, d): |
|
"""Return a dict for the globals.""" |
|
if not d: |
|
return self.globals |
|
return dict(self.globals, **d) |
|
|
|
|
|
class Template(object): |
|
"""The central template object. This class represents a compiled template |
|
and is used to evaluate it. |
|
|
|
Normally the template object is generated from an :class:`Environment` but |
|
it also has a constructor that makes it possible to create a template |
|
instance directly using the constructor. It takes the same arguments as |
|
the environment constructor but it's not possible to specify a loader. |
|
|
|
Every template object has a few methods and members that are guaranteed |
|
to exist. However it's important that a template object should be |
|
considered immutable. Modifications on the object are not supported. |
|
|
|
Template objects created from the constructor rather than an environment |
|
do have an `environment` attribute that points to a temporary environment |
|
that is probably shared with other templates created with the constructor |
|
and compatible settings. |
|
|
|
>>> template = Template('Hello {{ name }}!') |
|
>>> template.render(name='John Doe') |
|
u'Hello John Doe!' |
|
|
|
>>> stream = template.stream(name='John Doe') |
|
>>> stream.next() |
|
u'Hello John Doe!' |
|
>>> stream.next() |
|
Traceback (most recent call last): |
|
... |
|
StopIteration |
|
""" |
|
|
|
def __new__(cls, source, |
|
block_start_string=BLOCK_START_STRING, |
|
block_end_string=BLOCK_END_STRING, |
|
variable_start_string=VARIABLE_START_STRING, |
|
variable_end_string=VARIABLE_END_STRING, |
|
comment_start_string=COMMENT_START_STRING, |
|
comment_end_string=COMMENT_END_STRING, |
|
line_statement_prefix=LINE_STATEMENT_PREFIX, |
|
line_comment_prefix=LINE_COMMENT_PREFIX, |
|
trim_blocks=TRIM_BLOCKS, |
|
lstrip_blocks=LSTRIP_BLOCKS, |
|
newline_sequence=NEWLINE_SEQUENCE, |
|
keep_trailing_newline=KEEP_TRAILING_NEWLINE, |
|
extensions=(), |
|
optimized=True, |
|
undefined=Undefined, |
|
finalize=None, |
|
autoescape=False): |
|
env = get_spontaneous_environment( |
|
block_start_string, block_end_string, variable_start_string, |
|
variable_end_string, comment_start_string, comment_end_string, |
|
line_statement_prefix, line_comment_prefix, trim_blocks, |
|
lstrip_blocks, newline_sequence, keep_trailing_newline, |
|
frozenset(extensions), optimized, undefined, finalize, autoescape, |
|
None, 0, False, None) |
|
return env.from_string(source, template_class=cls) |
|
|
|
@classmethod |
|
def from_code(cls, environment, code, globals, uptodate=None): |
|
"""Creates a template object from compiled code and the globals. This |
|
is used by the loaders and environment to create a template object. |
|
""" |
|
namespace = { |
|
'environment': environment, |
|
'__file__': code.co_filename |
|
} |
|
exec(code, namespace) |
|
rv = cls._from_namespace(environment, namespace, globals) |
|
rv._uptodate = uptodate |
|
return rv |
|
|
|
@classmethod |
|
def from_module_dict(cls, environment, module_dict, globals): |
|
"""Creates a template object from a module. This is used by the |
|
module loader to create a template object. |
|
|
|
.. versionadded:: 2.4 |
|
""" |
|
return cls._from_namespace(environment, module_dict, globals) |
|
|
|
@classmethod |
|
def _from_namespace(cls, environment, namespace, globals): |
|
t = object.__new__(cls) |
|
t.environment = environment |
|
t.globals = globals |
|
t.name = namespace['name'] |
|
t.filename = namespace['__file__'] |
|
t.blocks = namespace['blocks'] |
|
|
|
# render function and module |
|
t.root_render_func = namespace['root'] |
|
t._module = None |
|
|
|
# debug and loader helpers |
|
t._debug_info = namespace['debug_info'] |
|
t._uptodate = None |
|
|
|
# store the reference |
|
namespace['environment'] = environment |
|
namespace['__jinja_template__'] = t |
|
|
|
return t |
|
|
|
def render(self, *args, **kwargs): |
|
"""This method accepts the same arguments as the `dict` constructor: |
|
A dict, a dict subclass or some keyword arguments. If no arguments |
|
are given the context will be empty. These two calls do the same:: |
|
|
|
template.render(knights='that say nih') |
|
template.render({'knights': 'that say nih'}) |
|
|
|
This will return the rendered template as unicode string. |
|
""" |
|
vars = dict(*args, **kwargs) |
|
try: |
|
return concat(self.root_render_func(self.new_context(vars))) |
|
except Exception: |
|
exc_info = sys.exc_info() |
|
return self.environment.handle_exception(exc_info, True) |
|
|
|
def stream(self, *args, **kwargs): |
|
"""Works exactly like :meth:`generate` but returns a |
|
:class:`TemplateStream`. |
|
""" |
|
return TemplateStream(self.generate(*args, **kwargs)) |
|
|
|
def generate(self, *args, **kwargs): |
|
"""For very large templates it can be useful to not render the whole |
|
template at once but evaluate each statement after another and yield |
|
piece for piece. This method basically does exactly that and returns |
|
a generator that yields one item after another as unicode strings. |
|
|
|
It accepts the same arguments as :meth:`render`. |
|
""" |
|
vars = dict(*args, **kwargs) |
|
try: |
|
for event in self.root_render_func(self.new_context(vars)): |
|
yield event |
|
except Exception: |
|
exc_info = sys.exc_info() |
|
else: |
|
return |
|
yield self.environment.handle_exception(exc_info, True) |
|
|
|
def new_context(self, vars=None, shared=False, locals=None): |
|
"""Create a new :class:`Context` for this template. The vars |
|
provided will be passed to the template. Per default the globals |
|
are added to the context. If shared is set to `True` the data |
|
is passed as it to the context without adding the globals. |
|
|
|
`locals` can be a dict of local variables for internal usage. |
|
""" |
|
return new_context(self.environment, self.name, self.blocks, |
|
vars, shared, self.globals, locals) |
|
|
|
def make_module(self, vars=None, shared=False, locals=None): |
|
"""This method works like the :attr:`module` attribute when called |
|
without arguments but it will evaluate the template on every call |
|
rather than caching it. It's also possible to provide |
|
a dict which is then used as context. The arguments are the same |
|
as for the :meth:`new_context` method. |
|
""" |
|
return TemplateModule(self, self.new_context(vars, shared, locals)) |
|
|
|
@property |
|
def module(self): |
|
"""The template as module. This is used for imports in the |
|
template runtime but is also useful if one wants to access |
|
exported template variables from the Python layer: |
|
|
|
>>> t = Template('{% macro foo() %}42{% endmacro %}23') |
|
>>> unicode(t.module) |
|
u'23' |
|
>>> t.module.foo() |
|
u'42' |
|
""" |
|
if self._module is not None: |
|
return self._module |
|
self._module = rv = self.make_module() |
|
return rv |
|
|
|
def get_corresponding_lineno(self, lineno): |
|
"""Return the source line number of a line number in the |
|
generated bytecode as they are not in sync. |
|
""" |
|
for template_line, code_line in reversed(self.debug_info): |
|
if code_line <= lineno: |
|
return template_line |
|
return 1 |
|
|
|
@property |
|
def is_up_to_date(self): |
|
"""If this variable is `False` there is a newer version available.""" |
|
if self._uptodate is None: |
|
return True |
|
return self._uptodate() |
|
|
|
@property |
|
def debug_info(self): |
|
"""The debug info mapping.""" |
|
return [tuple(imap(int, x.split('='))) for x in |
|
self._debug_info.split('&')] |
|
|
|
def __repr__(self): |
|
if self.name is None: |
|
name = 'memory:%x' % id(self) |
|
else: |
|
name = repr(self.name) |
|
return '<%s %s>' % (self.__class__.__name__, name) |
|
|
|
|
|
@implements_to_string |
|
class TemplateModule(object): |
|
"""Represents an imported template. All the exported names of the |
|
template are available as attributes on this object. Additionally |
|
converting it into an unicode- or bytestrings renders the contents. |
|
""" |
|
|
|
def __init__(self, template, context): |
|
self._body_stream = list(template.root_render_func(context)) |
|
self.__dict__.update(context.get_exported()) |
|
self.__name__ = template.name |
|
|
|
def __html__(self): |
|
return Markup(concat(self._body_stream)) |
|
|
|
def __str__(self): |
|
return concat(self._body_stream) |
|
|
|
def __repr__(self): |
|
if self.__name__ is None: |
|
name = 'memory:%x' % id(self) |
|
else: |
|
name = repr(self.__name__) |
|
return '<%s %s>' % (self.__class__.__name__, name) |
|
|
|
|
|
class TemplateExpression(object): |
|
"""The :meth:`jinja2.Environment.compile_expression` method returns an |
|
instance of this object. It encapsulates the expression-like access |
|
to the template with an expression it wraps. |
|
""" |
|
|
|
def __init__(self, template, undefined_to_none): |
|
self._template = template |
|
self._undefined_to_none = undefined_to_none |
|
|
|
def __call__(self, *args, **kwargs): |
|
context = self._template.new_context(dict(*args, **kwargs)) |
|
consume(self._template.root_render_func(context)) |
|
rv = context.vars['result'] |
|
if self._undefined_to_none and isinstance(rv, Undefined): |
|
rv = None |
|
return rv |
|
|
|
|
|
@implements_iterator |
|
class TemplateStream(object): |
|
"""A template stream works pretty much like an ordinary python generator |
|
but it can buffer multiple items to reduce the number of total iterations. |
|
Per default the output is unbuffered which means that for every unbuffered |
|
instruction in the template one unicode string is yielded. |
|
|
|
If buffering is enabled with a buffer size of 5, five items are combined |
|
into a new unicode string. This is mainly useful if you are streaming |
|
big templates to a client via WSGI which flushes after each iteration. |
|
""" |
|
|
|
def __init__(self, gen): |
|
self._gen = gen |
|
self.disable_buffering() |
|
|
|
def dump(self, fp, encoding=None, errors='strict'): |
|
"""Dump the complete stream into a file or file-like object. |
|
Per default unicode strings are written, if you want to encode |
|
before writing specify an `encoding`. |
|
|
|
Example usage:: |
|
|
|
Template('Hello {{ name }}!').stream(name='foo').dump('hello.html') |
|
""" |
|
close = False |
|
if isinstance(fp, string_types): |
|
fp = open(fp, encoding is None and 'w' or 'wb') |
|
close = True |
|
try: |
|
if encoding is not None: |
|
iterable = (x.encode(encoding, errors) for x in self) |
|
else: |
|
iterable = self |
|
if hasattr(fp, 'writelines'): |
|
fp.writelines(iterable) |
|
else: |
|
for item in iterable: |
|
fp.write(item) |
|
finally: |
|
if close: |
|
fp.close() |
|
|
|
def disable_buffering(self): |
|
"""Disable the output buffering.""" |
|
self._next = get_next(self._gen) |
|
self.buffered = False |
|
|
|
def enable_buffering(self, size=5): |
|
"""Enable buffering. Buffer `size` items before yielding them.""" |
|
if size <= 1: |
|
raise ValueError('buffer size too small') |
|
|
|
def generator(next): |
|
buf = [] |
|
c_size = 0 |
|
push = buf.append |
|
|
|
while 1: |
|
try: |
|
while c_size < size: |
|
c = next() |
|
push(c) |
|
if c: |
|
c_size += 1 |
|
except StopIteration: |
|
if not c_size: |
|
return |
|
yield concat(buf) |
|
del buf[:] |
|
c_size = 0 |
|
|
|
self.buffered = True |
|
self._next = get_next(generator(get_next(self._gen))) |
|
|
|
def __iter__(self): |
|
return self |
|
|
|
def __next__(self): |
|
return self._next() |
|
|
|
|
|
# hook in default template class. if anyone reads this comment: ignore that |
|
# it's possible to use custom templates ;-) |
|
Environment.template_class = Template
|
|
|