revamped jinja2 import system. the behavior is less confusing now, but it's not backwards compatible. I like it though ;)
--HG--
branch : trunk
diff --git a/jinja2/compiler.py b/jinja2/compiler.py
index 542beed..958b2c3 100644
--- a/jinja2/compiler.py
+++ b/jinja2/compiler.py
@@ -210,14 +210,15 @@
self.identifiers.tests.add(node.name)
def visit_Macro(self, node):
- """Macros set local."""
self.identifiers.declared_locally.add(node.name)
- def visit_Include(self, node):
- """Some includes set local."""
+ def visit_Import(self, node):
self.generic_visit(node)
- if node.target is not None:
- self.identifiers.declared_locally.add(node.target)
+ self.identifiers.declared_locally.add(node.target)
+
+ def visit_FromImport(self, node):
+ self.generic_visit(node)
+ self.identifiers.declared_locally.update(node.names)
def visit_Assign(self, node):
"""Visit assignments in the correct order."""
@@ -232,7 +233,8 @@
class CompilerExit(Exception):
"""Raised if the compiler encountered a situation where it just
doesn't make sense to further process the code. Any block that
- raises such an exception is not further processed."""
+ raises such an exception is not further processed.
+ """
class CodeGenerator(NodeVisitor):
@@ -597,28 +599,11 @@
def visit_Include(self, node, frame):
"""Handles includes."""
- # simpled include is include into a variable. This kind of
- # include works the same on every level, so we handle it first.
- if node.target is not None:
- self.writeline('l_%s = ' % node.target, node)
- if frame.toplevel:
- self.write('context[%r] = ' % node.target)
- self.write('environment.get_template(')
- self.visit(node.template, frame)
- self.write(', %r).include(context)' % self.name)
- return
-
self.writeline('included_template = environment.get_template(', node)
self.visit(node.template, frame)
self.write(', %r)' % self.name)
- if frame.toplevel:
- self.writeline('included_context = included_template.new_context('
- 'context.get_root())')
- self.writeline('for event in included_template.root_render_func('
- 'included_context):')
- else:
- self.writeline('for event in included_template.root_render_func('
- 'included_template.new_context(context.get_root())):')
+ self.writeline('for event in included_template.root_render_func('
+ 'included_template.new_context(context.get_root())):')
self.indent()
if frame.buffer is None:
self.writeline('yield event')
@@ -626,11 +611,33 @@
self.writeline('%s.append(event)' % frame.buffer)
self.outdent()
- # if we have a toplevel include the exported variables are copied
- # into the current context without exporting them. context.udpate
- # does *not* mark the variables as exported
+ def visit_Import(self, node, frame):
+ """Visit regular imports."""
+ self.writeline('l_%s = ' % node.target, node)
if frame.toplevel:
- self.writeline('context.update(included_context.get_exported())')
+ self.write('context[%r] = ' % node.target)
+ self.write('environment.get_template(')
+ self.visit(node.template, frame)
+ self.write(', %r).include(context)' % self.name)
+
+ def visit_FromImport(self, node, frame):
+ """Visit named imports."""
+ self.newline(node)
+ self.write('included_template = environment.get_template(')
+ self.visit(node.template, frame)
+ self.write(', %r).include(context)' % self.name)
+ for name in node.names:
+ self.writeline('l_%s = getattr(included_template, '
+ '%r, missing)' % (name, name))
+ self.writeline('if l_%s is missing:' % name)
+ self.indent()
+ self.writeline('l_%s = environment.undefined(%r %% '
+ 'included_template.name)' %
+ (name, 'the template %r does not export '
+ 'the requested name ' + repr(name)))
+ self.outdent()
+ if frame.toplevel:
+ self.writeline('context[%r] = l_%s' % (name, name))
def visit_For(self, node, frame):
loop_frame = frame.inner()
@@ -1022,6 +1029,9 @@
def visit_Filter(self, node, frame, initial=None):
self.write('f_%s(' % node.name)
func = self.environment.filters.get(node.name)
+ if func is None:
+ raise TemplateAssertionError('no filter named %r' % node.name,
+ node.lineno, self.filename)
if getattr(func, 'contextfilter', False):
self.write('context, ')
elif getattr(func, 'environmentfilter', False):
@@ -1037,9 +1047,9 @@
def visit_Test(self, node, frame):
self.write('t_%s(' % node.name)
- func = self.environment.tests.get(node.name)
- if getattr(func, 'contexttest', False):
- self.write('context, ')
+ if node.name not in self.environment.tests:
+ raise TemplateAssertionError('no test named %r' % node.name,
+ node.lineno, self.filename)
self.visit(node.node, frame)
self.signature(node, frame)
self.write(')')