.. automethod:: stream([context])
-.. autoclass:: jinja2.environment.TemplateStream
+.. autoclass:: jinja2.environment.TemplateStream()
:members: disable_buffering, enable_buffering
The Context
-----------
-.. autoclass:: jinja2.runtime.Context
+.. autoclass:: jinja2.runtime.Context()
:members: resolve, get_exported, get_all
.. attribute:: parent
.. _Babel: http://babel.edgewall.org/
-do
-~~
+Expression Statement
+--------------------
**Import name:** `jinja2.ext.do`
-The do aka expression-statement extension adds a simple `do` tag to the
+The "do" aka expression-statement extension adds a simple `do` tag to the
template engine that works like a variable expression but ignores the
return value.
.. _loopcontrols-extension:
-loopcontrols
-~~~~~~~~~~~~
+Loop Controls
+-------------
**Import name:** `jinja2.ext.loopcontrols`
env = Environment(extensions=[FragmentCacheExtension])
env.fragment_cache = SimpleCache()
+Inside the template it's then possible to mark blocks as cacheable. The
+following example caches a sidebar for 300 seconds:
+
+.. sourcecode:: html+jinja
+
+ {% cache 'sidebar', 300 %}
+ <div class="sidebar">
+ ...
+ </div>
+ {% endcache %}
+
.. _Werkzeug: http://werkzeug.pocoo.org/
Extension API
self.indent()
if have_extends:
self.writeline('parent_template = None')
- self.pull_locals(frame)
- self.pull_dependencies(node.body)
if 'self' in find_undeclared(node.body, ('self',)):
frame.identifiers.add_special('self')
self.writeline('l_self = TemplateReference(context)')
+ self.pull_locals(frame)
+ self.pull_dependencies(node.body)
self.blockvisit(node.body, frame)
self.outdent()
self.write('environment.' + node.name)
def visit_ExtensionAttribute(self, node, frame):
- self.write('environment.extensions[%r].%s' % (node.identifier, node.attr))
+ self.write('environment.extensions[%r].%s' % (node.identifier, node.name))
def visit_ImportedName(self, node, frame):
self.write(self.import_aliases[node.importname])
def visit_InternalName(self, node, frame):
self.write(node.name)
+ def visit_ContextReference(self, node, frame):
+ self.write('context')
+
def visit_Continue(self, node, frame):
self.writeline('continue', node)
This node is usually constructed by calling the
:meth:`~jinja2.ext.Extension.attr` method on an extension.
"""
- fields = ('identifier', 'attr')
+ fields = ('identifier', 'name')
class ImportedName(Expr):
return Markup(self.expr.as_const())
+class ContextReference(Expr):
+ """Returns the current template context."""
+
+
class Continue(Stmt):
"""Continue a loop."""
def visit_If(self, node):
"""Eliminate dead code."""
+ # do not optimize ifs that have a block inside so that it doesn't
+ # break super().
+ if node.find(nodes.Block) is not None:
+ return self.generic_visit(node)
try:
val = self.visit(node.test).as_const()
except nodes.Impossible:
self.__context = context
def __getitem__(self, name):
- func = self.__context.blocks[name][-1]
+ func = self.__context.blocks[name][0]
wrap = self.__context.environment.autoescape and \
Markup or (lambda x: x)
render = lambda: wrap(concat(func(self.__context)))
:copyright: 2008 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
-from jinja2 import Environment
+from jinja2 import Environment, nodes
+from jinja2.ext import Extension
+
+
+importable_object = 23
+
+
+class TestExtension(Extension):
+ tags = set(['test'])
+ ext_attr = 42
+
+ def parse(self, parser):
+ return nodes.Output([self.call_method('_dump', [
+ nodes.EnvironmentAttribute('sandboxed'),
+ self.attr('ext_attr'),
+ nodes.ImportedName(__name__ + '.importable_object'),
+ nodes.ContextReference()
+ ])]).set_lineno(parser.stream.next().lineno)
+
+ def _dump(self, sandboxed, ext_attr, imported_object, context):
+ return '%s|%s|%s|%s' % (
+ sandboxed,
+ ext_attr,
+ imported_object,
+ context.blocks
+ )
def test_loop_controls():
{%- do items.append(loop.index0 ~ char) %}
{%- endfor %}{{ items|join(', ') }}''')
assert tmpl.render() == '0f, 1o, 2o'
+
+
+def test_extension_nodes():
+ env = Environment(extensions=[TestExtension])
+ tmpl = env.from_string('{% test %}')
+ assert tmpl.render() == 'False|42|23|{}'
{% for item in [1] if loop.index == 0 %}...{% endfor %}'''
LOOPERROR2 = '''\
{% for item in [] %}...{% else %}{{ loop }}{% endfor %}'''
+LOOPFILTER = '''\
+{% for item in range(10) if item is even %}[{{ item }}]{% endfor %}'''
+EXTENDEDLOOPFILTER = '''\
+{% for item in range(10) if item is even %}[{{ loop.index
+}}:{{ item }}]{% endfor %}'''
def test_simple(env):
raises(UndefinedError, tmpl.render)
tmpl = env.from_string(LOOPERROR2)
assert tmpl.render() == ''
+
+
+def test_loop_filter(env):
+ tmpl = env.from_string(LOOPFILTER)
+ assert tmpl.render() == '[0][2][4][6][8]'
+ tmpl = env.from_string(EXTENDEDLOOPFILTER)
+ assert tmpl.render() == '[1:0][2:2][3:4][4:6][5:8]'
def test_empty(env):
tmpl = env.from_string(EMPTY)
assert tmpl.render() == '[]'
+
+
+def test_complete(env):
+ tmpl = env.from_string('{% if a %}A{% elif b %}B{% elif c == d %}'
+ 'C{% else %}D{% endif %}')
+ assert tmpl.render(a=0, b=False, c=42, d=42.0) == 'C'
+
+
+def test_no_scope(env):
+ tmpl = env.from_string('{% if a %}{% set foo = 1 %}{% endif %}{{ foo }}')
+ assert tmpl.render(a=True) == '1'
+ tmpl = env.from_string('{% if true %}{% set foo = 1 %}{% endif %}{{ foo }}')
+ assert tmpl.render() == '1'
def test_reuse_blocks(env):
tmpl = env.from_string('{{ self.foo() }}|{% block foo %}42{% endblock %}|{{ self.foo() }}')
assert tmpl.render() == '42|42|42'
+
+
+def test_preserve_blocks():
+ env = Environment(loader=DictLoader({
+ 'a': '{% if false %}{% block x %}A{% endblock %}{% endif %}{{ self.x() }}',
+ 'b': '{% extends "a" %}{% block x %}B{{ super() }}{% endblock %}'
+ }))
+ tmpl = env.get_template('b')
+ assert tmpl.render() == 'BA'
:copyright: 2007 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
-from jinja2.sandbox import SandboxedEnvironment, unsafe
+from jinja2.sandbox import SandboxedEnvironment, \
+ ImmutableSandboxedEnvironment, unsafe
class PrivateStuff(object):
...
TemplateSyntaxError: expected token 'in', got '.' (line 1)
'''
+
+
+test_immutable_environment = '''
+>>> env = MODULE.ImmutableSandboxedEnvironment()
+>>> env.from_string('{{ [].append(23) }}').render()
+Traceback (most recent call last):
+ ...
+SecurityError: access to attribute 'append' of 'list' object is unsafe.
+>>> env.from_string('{{ {1:2}.clear() }}').render()
+Traceback (most recent call last):
+ ...
+SecurityError: access to attribute 'clear' of 'dict' object is unsafe.
+'''