Body
{% include 'footer.html' %}
-Included templates have access to the current template variables minus local
-modifications.
+Included templates have access to the variables of the active context by
+default. For more details about context behavior of imports and includes
+see :ref:`import-visibility`.
.. _import:
different templates and get imported from there. This works similar to the
import statements in Python. It's important to know that imports are cached
and imported templates don't have access to the current template variables,
-just the globals.
+just the globals by defualt. For more details about context behavior of
+imports and includes see :ref:`import-visibility`.
There are two ways to import templates. You can import the complete template
into a variable or request specific macros / exported variables from it.
<p>{{ textarea('comment') }}</p>
+.. _import-visibility:
+
+Import Context Behavior
+-----------------------
+
+Per default included templates are passed the current context and imported
+templates not. The reason for this is that imports unlike includes are
+cached as imports are often used just as a module that holds macros.
+
+This however can be changed of course explicitly. By adding `with context`
+or `without context` to the import/include directive the current context
+can be passed to the template and caching is disabled automatically.
+
+Here two examples::
+
+ {% from 'forms.html' import input with context %}
+ {% include 'header.html' without context %}
+
+
.. _expressions:
Expressions
def visit_Include(self, node, frame):
"""Handles includes."""
- self.writeline('included_template = environment.get_template(', node)
- self.visit(node.template, frame)
- self.write(', %r)' % self.name)
- self.writeline('for event in included_template.root_render_func('
- 'included_template.new_context(context.parent, True)):')
+ if node.with_context:
+ self.writeline('template = environment.get_template(', node)
+ self.visit(node.template, frame)
+ self.write(', %r)' % self.name)
+ self.writeline('for event in template.root_render_func('
+ 'template.new_context(context.parent, True)):')
+ else:
+ self.writeline('for event in environment.get_template(', node)
+ self.visit(node.template, frame)
+ self.write(', %r).module._TemplateModule__body_stream:' %
+ self.name)
self.indent()
if frame.buffer is None:
self.writeline('yield event')
self.write('context.vars[%r] = ' % node.target)
self.write('environment.get_template(')
self.visit(node.template, frame)
- self.write(', %r).module' % self.name)
+ self.write(', %r).' % self.name)
+ if node.with_context:
+ self.write('make_module(context.parent, True)')
+ else:
+ self.write('module')
if frame.toplevel and not node.target.startswith('__'):
self.writeline('context.exported_vars.discard(%r)' % node.target)
self.newline(node)
self.write('included_template = environment.get_template(')
self.visit(node.template, frame)
- self.write(', %r).module' % self.name)
+ self.write(', %r).' % self.name)
+ if node.with_context:
+ self.write('make_module(context.parent, True)')
+ else:
+ self.write('module')
for name in node.names:
if isinstance(name, tuple):
name, alias = name
parent = dict(self.globals, **vars)
return Context(self.environment, parent, self.name, self.blocks)
+ def make_module(self, vars=None, shared=False):
+ """Like the `module` property but always reevaluates the template
+ and it's possible to provide a context.
+ """
+ return TemplateModule(self, self.new_context(vars, shared))
+
@property
def module(self):
"""The template as module. This is used for imports in the
"""
if hasattr(self, '_module'):
return self._module
- self._module = rv = TemplateModule(self, self.new_context())
+ self._module = rv = self.make_module()
return rv
def get_corresponding_lineno(self, lineno):
"""
def __init__(self, template, context):
+ # don't alter this attribute unless you change it in the
+ # compiler too. The Include without context passing directly
+ # uses the mangled name. The reason why we use a mangled one
+ # is to avoid name clashes with macros with those names.
self.__body_stream = tuple(template.root_render_func(context))
self.__dict__.update(context.get_exported())
self.__name__ = template.name
self.current = old_token
return result
- def skip(self, n):
+ def skip(self, n=1):
"""Got n tokens ahead."""
for x in xrange(n):
self.next()
class Include(Stmt):
"""A node that represents the include tag."""
- fields = ('template',)
+ fields = ('template', 'with_context')
class Import(Stmt):
"""A node that represents the import tag."""
- fields = ('template', 'target')
+ fields = ('template', 'target', 'with_context')
class FromImport(Stmt):
The list of names may contain tuples if aliases are wanted.
"""
- fields = ('template', 'names')
+ fields = ('template', 'names', 'with_context')
class Trans(Stmt):
"""Parse an if construct."""
node = result = nodes.If(lineno=self.stream.expect('name:if').lineno)
while 1:
- # TODO: exclude conditional expressions here
- node.test = self.parse_tuple()
+ node.test = self.parse_tuple(no_condexpr=True)
node.body = self.parse_statements(('name:elif', 'name:else',
'name:endif'))
token = self.stream.next()
node.template = self.parse_expression()
return node
+ def parse_import_context(self, node, default):
+ if (self.stream.current.test('name:with') or
+ self.stream.current.test('name:without')) and \
+ self.stream.look().test('name:context'):
+ node.with_context = self.stream.next().value == 'with'
+ self.stream.skip()
+ else:
+ node.with_context = default
+ return node
+
def parse_include(self):
node = nodes.Include(lineno=self.stream.next().lineno)
node.template = self.parse_expression()
- return node
+ return self.parse_import_context(node, True)
def parse_import(self):
node = nodes.Import(lineno=self.stream.next().lineno)
raise TemplateSyntaxError('can\'t assign imported template '
'to %r' % node.target, node.lineno,
self.filename)
- return node
+ return self.parse_import_context(node, False)
def parse_from(self):
node = nodes.FromImport(lineno=self.stream.next().lineno)
node.template = self.parse_expression()
self.stream.expect('name:import')
node.names = []
+
+ def parse_context():
+ if self.stream.current.value in ('with', 'without') and \
+ self.stream.look().test('name:context'):
+ node.with_context = self.stream.next().value == 'with'
+ self.stream.skip()
+ return True
+ return False
+
while 1:
if node.names:
self.stream.expect('comma')
if self.stream.current.type is 'name':
+ if parse_context():
+ break
target = nodes.Name(self.stream.current.value, 'store')
if not target.can_assign():
raise TemplateSyntaxError('can\'t import object named %r'
node.names.append((target.name, alias.value))
else:
node.names.append(target.name)
- if self.stream.current.type is not 'comma':
+ if parse_context() or self.stream.current.type is not 'comma':
break
else:
break
- if self.stream.current.type is 'comma':
- self.stream.next()
+ if not hasattr(node, 'with_context'):
+ node.with_context = False
+ if self.stream.current.type is 'comma':
+ self.stream.next()
return node
def parse_signature(self, node):