added "with context" or "without context" import/include modifiers
--HG--
branch : trunk
diff --git a/jinja2/compiler.py b/jinja2/compiler.py
index faa8b41..3d4fcbe 100644
--- a/jinja2/compiler.py
+++ b/jinja2/compiler.py
@@ -701,11 +701,17 @@
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')
@@ -720,7 +726,11 @@
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)
@@ -729,7 +739,11 @@
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
diff --git a/jinja2/environment.py b/jinja2/environment.py
index ef916a4..68571fd 100644
--- a/jinja2/environment.py
+++ b/jinja2/environment.py
@@ -529,6 +529,12 @@
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
@@ -543,7 +549,7 @@
"""
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):
@@ -583,6 +589,10 @@
"""
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
diff --git a/jinja2/lexer.py b/jinja2/lexer.py
index ade9f92..3b65b95 100644
--- a/jinja2/lexer.py
+++ b/jinja2/lexer.py
@@ -218,7 +218,7 @@
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()
diff --git a/jinja2/nodes.py b/jinja2/nodes.py
index b3255d5..d0372e8 100644
--- a/jinja2/nodes.py
+++ b/jinja2/nodes.py
@@ -265,12 +265,12 @@
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):
@@ -284,7 +284,7 @@
The list of names may contain tuples if aliases are wanted.
"""
- fields = ('template', 'names')
+ fields = ('template', 'names', 'with_context')
class Trans(Stmt):
diff --git a/jinja2/parser.py b/jinja2/parser.py
index 84b110c..4239e25 100644
--- a/jinja2/parser.py
+++ b/jinja2/parser.py
@@ -123,8 +123,7 @@
"""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()
@@ -152,10 +151,20 @@
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)
@@ -166,17 +175,28 @@
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'
@@ -198,12 +218,14 @@
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):