395 lines
16 KiB
Python
395 lines
16 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
jinja.environment
|
|
~~~~~~~~~~~~~~~~~
|
|
|
|
Provides a class that holds runtime and parsing time options.
|
|
|
|
:copyright: 2007 by Armin Ronacher.
|
|
:license: BSD, see LICENSE for more details.
|
|
"""
|
|
from jinja.lexer import Lexer
|
|
from jinja.parser import Parser
|
|
from jinja.loaders import LoaderWrapper
|
|
from jinja.datastructure import SilentUndefined, Markup, Context, FakeTranslator
|
|
from jinja.utils import collect_translations, get_attribute
|
|
from jinja.exceptions import FilterNotFound, TestNotFound, \
|
|
SecurityException, TemplateSyntaxError
|
|
from jinja.defaults import DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE
|
|
|
|
|
|
__all__ = ['Environment']
|
|
|
|
|
|
#: minor speedup
|
|
_getattr = getattr
|
|
|
|
|
|
class Environment(object):
|
|
"""
|
|
The Jinja environment.
|
|
|
|
The core component of Jinja is the `Environment`. It contains
|
|
important shared variables like configuration, filters, tests,
|
|
globals and others.
|
|
"""
|
|
|
|
def __init__(self,
|
|
block_start_string='{%',
|
|
block_end_string='%}',
|
|
variable_start_string='{{',
|
|
variable_end_string='}}',
|
|
comment_start_string='{#',
|
|
comment_end_string='#}',
|
|
trim_blocks=False,
|
|
auto_escape=False,
|
|
default_filters=None,
|
|
template_charset='utf-8',
|
|
charset='utf-8',
|
|
namespace=None,
|
|
loader=None,
|
|
filters=None,
|
|
tests=None,
|
|
context_class=Context,
|
|
undefined_singleton=SilentUndefined,
|
|
disable_regexps=False,
|
|
friendly_traceback=True,
|
|
translator_factory=None):
|
|
"""
|
|
Here the possible initialization parameters:
|
|
|
|
========================= ============================================
|
|
`block_start_string` * the string marking the begin of a block.
|
|
this 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 ``'{{'``.
|
|
`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 ``'#}'``.
|
|
`trim_blocks` * If this is set to ``True`` the first newline
|
|
after a block is removed (block, not
|
|
variable tag!). Defaults to ``False``.
|
|
`auto_escape` If this is set to ``True`` Jinja will
|
|
automatically escape all variables using xml
|
|
escaping methods. If you don't want to
|
|
escape a string you have to wrap it in a
|
|
``Markup`` object from the
|
|
``jinja.datastructure`` module. If
|
|
`auto_escape` is ``True`` there will be also
|
|
a ``Markup`` object in the template
|
|
namespace to define partial html fragments.
|
|
Note that we do not recommend this feature.
|
|
`default_filters` list of tuples in the form (``filter_name``,
|
|
``arguments``) where ``filter_name`` is the
|
|
name of a registered filter and
|
|
``arguments`` a tuple with the filter
|
|
arguments. The filters specified here will
|
|
always be applied when printing data to the
|
|
template. *new in Jinja 1.1*
|
|
`template_charset` The charset of the templates. Defaults
|
|
to ``'utf-8'``.
|
|
`charset` Charset of all string input data. Defaults
|
|
to ``'utf-8'``.
|
|
`namespace` Global namespace for all templates.
|
|
`loader` Specify a template loader.
|
|
`filters` dict of filters or the default filters if
|
|
not defined.
|
|
`tests` dict of tests of the default tests if not
|
|
defined.
|
|
`context_class` the context class this template should use.
|
|
See the `Context` documentation for more
|
|
details.
|
|
`undefined_singleton` The singleton value that is used for missing
|
|
variables. *new in Jinja 1.1*
|
|
`disable_regexps` Disable support for regular expresssions.
|
|
`friendly_traceback` Set this to `False` to disable the developer
|
|
friendly traceback rewriting. Whenever an
|
|
runtime or syntax error occours jinja will
|
|
try to make a developer friendly traceback
|
|
that shows the error in the template line.
|
|
This however can be annoying when debugging
|
|
broken functions that are called from the
|
|
template. *new in Jinja 1.1*
|
|
`translator_factory` A callback function that is called with
|
|
the context as first argument to get the
|
|
translator for the current instance.
|
|
*new in Jinja 1.2*
|
|
========================= ============================================
|
|
|
|
All of these variables except those marked with a star (*) are
|
|
modifiable after environment initialization.
|
|
"""
|
|
|
|
# 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.trim_blocks = trim_blocks
|
|
|
|
# other stuff
|
|
self.template_charset = template_charset
|
|
self.charset = charset
|
|
self.loader = loader
|
|
if filters is None:
|
|
filters = DEFAULT_FILTERS.copy()
|
|
self.filters = filters
|
|
if tests is None:
|
|
tests = DEFAULT_TESTS.copy()
|
|
self.tests = tests
|
|
self.default_filters = default_filters or []
|
|
self.context_class = context_class
|
|
self.undefined_singleton = undefined_singleton
|
|
self.disable_regexps = disable_regexps
|
|
self.friendly_traceback = friendly_traceback
|
|
|
|
# global namespace
|
|
if namespace is None:
|
|
namespace = DEFAULT_NAMESPACE.copy()
|
|
self.globals = namespace
|
|
|
|
# jinja 1.0 compatibility
|
|
if auto_escape:
|
|
self.default_filters.append(('escape', (True,)))
|
|
self.globals['Markup'] = Markup
|
|
|
|
# and here the translator factory
|
|
self.translator_factory = translator_factory
|
|
|
|
# create lexer
|
|
self.lexer = Lexer(self)
|
|
|
|
def loader(self, value):
|
|
"""
|
|
Get or set the template loader.
|
|
"""
|
|
self._loader = LoaderWrapper(self, value)
|
|
loader = property(lambda s: s._loader, loader, doc=loader.__doc__)
|
|
|
|
def parse(self, source, filename=None):
|
|
"""
|
|
Parse the sourcecode and return the abstract syntax tree. This tree
|
|
of nodes is used by the `translators`_ to convert the template into
|
|
executable source- or bytecode.
|
|
|
|
.. _translators: translators.txt
|
|
"""
|
|
parser = Parser(self, source, filename)
|
|
return parser.parse()
|
|
|
|
def lex(self, source, filename=None):
|
|
"""
|
|
Lex the given sourcecode and return a generator that yields tokens.
|
|
The stream returned is not usable for Jinja but can be used if
|
|
Jinja templates should be processed by other tools (for example
|
|
syntax highlighting etc)
|
|
|
|
The tuples are returned in the form ``(lineno, token, value)``.
|
|
"""
|
|
return self.lexer.tokeniter(source, filename)
|
|
|
|
def from_string(self, source):
|
|
"""
|
|
Load and parse a template source and translate it into eval-able
|
|
Python code. This code is wrapped within a `Template` class that
|
|
allows you to render it.
|
|
"""
|
|
from jinja.translators.python import PythonTranslator
|
|
try:
|
|
rv = PythonTranslator.process(self, Parser(self, source).parse(),
|
|
source)
|
|
except TemplateSyntaxError, e:
|
|
# on syntax errors rewrite the traceback if wanted
|
|
if not self.friendly_traceback:
|
|
raise
|
|
from jinja.debugger import raise_syntax_error
|
|
if __debug__:
|
|
__traceback_hide__ = True
|
|
raise_syntax_error(e, self, source)
|
|
else:
|
|
return rv
|
|
|
|
def get_template(self, filename):
|
|
"""
|
|
Load a template from a loader. If the template does not exist, you
|
|
will get a `TemplateNotFound` exception.
|
|
"""
|
|
return self._loader.load(filename)
|
|
|
|
def to_unicode(self, value):
|
|
"""
|
|
Convert a value to unicode with the rules defined on the environment.
|
|
"""
|
|
# undefined and None expand to ""
|
|
if value in (None, self.undefined_singleton):
|
|
return u''
|
|
# things that are already unicode can pass. As long as nobody
|
|
# does ugly things with the class it works for jinja too
|
|
elif isinstance(value, unicode):
|
|
return value
|
|
# otherwise try to use __unicode__ or decode __str__
|
|
try:
|
|
return unicode(value)
|
|
except UnicodeError:
|
|
return str(value).decode(self.charset, 'ignore')
|
|
|
|
def get_translator(self, context):
|
|
"""
|
|
Return the translator for i18n.
|
|
|
|
A translator is an object that provides the two functions
|
|
``gettext(string)`` and ``ngettext(singular, plural, n)``. Note
|
|
that both of them have to return unicode!
|
|
"""
|
|
if self.translator_factory is not None:
|
|
return self.translator_factory(context)
|
|
return FakeTranslator()
|
|
|
|
def get_translations(self, name):
|
|
"""
|
|
Load template `name` and return all translatable strings (note that
|
|
that it really just returns the strings form this template, not from
|
|
the parent or any included templates!)
|
|
"""
|
|
return collect_translations(self.loader.parse(name))
|
|
|
|
def get_translations_for_string(self, string):
|
|
"""
|
|
Like `get_translations`, but the translations are loaded from a
|
|
normal string that represents the template.
|
|
"""
|
|
return collect_translations(self.parse(string))
|
|
|
|
def apply_filters(self, value, context, filters):
|
|
"""
|
|
Apply a list of filters on the variable.
|
|
"""
|
|
# some traceback systems allow to skip frames. but allow
|
|
# disabling that via -O to not make things slow
|
|
if __debug__:
|
|
__traceback_hide__ = True
|
|
|
|
cache = context.cache
|
|
for key in filters:
|
|
if key in cache:
|
|
func = cache[key]
|
|
else:
|
|
filtername, args = key
|
|
if filtername not in self.filters:
|
|
raise FilterNotFound(filtername)
|
|
cache[key] = func = self.filters[filtername](*args)
|
|
value = func(self, context, value)
|
|
return value
|
|
|
|
def perform_test(self, context, testname, args, value):
|
|
"""
|
|
Perform a test on a variable.
|
|
"""
|
|
# some traceback systems allow to skip frames. but allow
|
|
# disabling that via -O to not make things slow
|
|
if __debug__:
|
|
__traceback_hide__ = True
|
|
|
|
key = (testname, args)
|
|
if key in context.cache:
|
|
func = context.cache[key]
|
|
else:
|
|
if testname not in self.tests:
|
|
raise TestNotFound(testname)
|
|
context.cache[key] = func = self.tests[testname](*args)
|
|
return not not func(self, context, value)
|
|
|
|
def get_attribute(self, obj, name):
|
|
"""
|
|
Get one attribute from an object.
|
|
"""
|
|
# some traceback systems allow to skip frames. but allow
|
|
# disabling that via -O to not make things slow
|
|
if __debug__:
|
|
__traceback_hide__ = True
|
|
|
|
try:
|
|
return obj[name]
|
|
except (TypeError, KeyError, IndexError, AttributeError):
|
|
try:
|
|
return get_attribute(obj, name)
|
|
except (AttributeError, SecurityException):
|
|
pass
|
|
if obj is self.undefined_singleton:
|
|
return _getattr(obj, name)
|
|
return self.undefined_singleton
|
|
|
|
def get_attributes(self, obj, attributes):
|
|
"""
|
|
Get some attributes from an object. If attributes is an
|
|
empty sequence the object is returned as it.
|
|
"""
|
|
get = self.get_attribute
|
|
for name in attributes:
|
|
obj = get(obj, name)
|
|
return obj
|
|
|
|
def call_function(self, f, context, args, kwargs, dyn_args, dyn_kwargs):
|
|
"""
|
|
Function call helper. Called for all functions that are passed
|
|
any arguments.
|
|
"""
|
|
# some traceback systems allow to skip frames. but allow
|
|
# disabling that via -O to not make things slow
|
|
if __debug__:
|
|
__traceback_hide__ = True
|
|
|
|
if dyn_args is not None:
|
|
args += tuple(dyn_args)
|
|
if dyn_kwargs is not None:
|
|
kwargs.update(dyn_kwargs)
|
|
if _getattr(f, 'jinja_unsafe_call', False) or \
|
|
_getattr(f, 'alters_data', False):
|
|
return self.undefined_singleton
|
|
if _getattr(f, 'jinja_context_callable', False):
|
|
args = (self, context) + args
|
|
return f(*args, **kwargs)
|
|
|
|
def call_function_simple(self, f, context):
|
|
"""
|
|
Function call without arguments. Because of the smaller signature and
|
|
fewer logic here we have a bit of redundant code.
|
|
"""
|
|
# some traceback systems allow to skip frames. but allow
|
|
# disabling that via -O to not make things slow
|
|
if __debug__:
|
|
__traceback_hide__ = True
|
|
|
|
if _getattr(f, 'jinja_unsafe_call', False) or \
|
|
_getattr(f, 'alters_data', False):
|
|
return self.undefined_singleton
|
|
if _getattr(f, 'jinja_context_callable', False):
|
|
return f(self, context)
|
|
return f()
|
|
|
|
def finish_var(self, value, ctx):
|
|
"""
|
|
As long as no write_var function is passed to the template
|
|
evaluator the source generated by the python translator will
|
|
call this function for all variables.
|
|
"""
|
|
# some traceback systems allow to skip frames. but allow
|
|
# disabling that via -O to not make things slow
|
|
if __debug__:
|
|
__traceback_hide__ = True
|
|
|
|
if value is None:
|
|
return u''
|
|
elif value is self.undefined_singleton:
|
|
return unicode(value)
|
|
elif _getattr(value, 'jinja_no_finalization', False):
|
|
return value
|
|
val = self.to_unicode(value)
|
|
if self.default_filters:
|
|
val = self.apply_filters(val, ctx, self.default_filters)
|
|
return val
|