blob: 9943afc1317ab06fb3a5a7747c7d415c4f3b1cbe [file] [log] [blame]
# -*- 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.
"""
import re
from jinja.lexer import Lexer
from jinja.parser import Parser
from jinja.loaders import LoaderWrapper
from jinja.datastructure import Undefined, Context, Markup
from jinja.utils import escape
from jinja.exceptions import FilterNotFound, TestNotFound, SecurityException
from jinja.defaults import DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE
class Environment(object):
"""
The jinja environment.
"""
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,
template_charset='utf-8',
charset='utf-8',
namespace=None,
loader=None,
filters=None,
tests=None,
context_class=Context):
# 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
self.filters = filters is None and DEFAULT_FILTERS.copy() or filters
self.tests = tests is None and DEFAULT_TESTS.copy() or tests
self.auto_escape = auto_escape
self.context_class = context_class
# global namespace
self.globals = namespace is None and DEFAULT_NAMESPACE.copy() \
or namespace
# 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, loader.__doc__)
def parse(self, source, filename=None):
"""Function that creates a new parser and parses the source."""
parser = Parser(self, source, filename)
return parser.parse()
def from_string(self, source):
"""Load a template from a string."""
from jinja.parser import Parser
from jinja.translators.python import PythonTranslator
return PythonTranslator.process(self, Parser(self, source).parse())
def get_template(self, filename):
"""Load a template from a filename. Only works
if a proper loader is set."""
return self._loader.load(filename)
def to_unicode(self, value):
"""
Convert a value to unicode with the rules defined on the environment.
"""
if value in (None, Undefined):
return u''
elif isinstance(value, unicode):
return value
else:
try:
return unicode(value)
except UnicodeError:
return str(value).decode(self.charset, 'ignore')
def apply_filters(self, value, context, filters):
"""
Apply a list of filters on the variable.
"""
for key in filters:
if key in context.cache:
func = context.cache[key]
else:
filtername, args = key
if filtername not in self.filters:
raise FilterNotFound(filtername)
context.cache[key] = func = self.filters[filtername](*args)
value = func(self, context, value)
return value
def perform_test(self, context, testname, args, value, invert):
"""
Perform a test on a variable.
"""
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)
rv = func(self, context, value)
if invert:
return not rv
return bool(rv)
def get_attribute(self, obj, attributes):
"""
Get some attributes from an object.
"""
node = obj
for name in attributes:
try:
node = node[name]
except (TypeError, KeyError, IndexError):
if not hasattr(obj, name):
return Undefined
r = getattr(obj, 'jinja_allowed_attributes', None)
if r is not None and name not in r:
raise SecurityException('unsafe attributed %r accessed' % name)
node = getattr(obj, name)
return node
def call_function(self, f, args, kwargs, dyn_args, dyn_kwargs):
"""
Function call helper. Called for all functions that are passed
any arguments.
"""
if dyn_args is not None:
args += tuple(dyn_args)
elif dyn_kwargs is not None:
kwargs.update(dyn_kwargs)
if getattr(f, 'jinja_unsafe_call', False) or \
getattr(f, 'alters_data', False):
raise SecurityException('unsafe function %r called' % f.__name__)
return f(*args, **kwargs)
def call_function_simple(self, f):
"""
Function call without arguments.
"""
if getattr(f, 'jinja_unsafe_call', False) or \
getattr(f, 'alters_data', False):
raise SecurityException('unsafe function %r called' % f.__name__)
return f()
def finish_var(self, value):
"""
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.
"""
if value is Undefined or value is None:
return u''
elif isinstance(value, (int, float, Markup, bool)):
return unicode(value)
elif not isinstance(value, unicode):
value = self.to_unicode(value)
if self.auto_escape:
return escape(value, True)
return value