oohEmbed/app/jinja/environment.py
2009-07-18 11:32:16 +08:00

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