added "with context" or "without context" import/include modifiers

--HG--
branch : trunk
diff --git a/docs/templates.rst b/docs/templates.rst
index 8e5b023..18bd422 100644
--- a/docs/templates.rst
+++ b/docs/templates.rst
@@ -552,8 +552,9 @@
         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:
 
@@ -564,7 +565,8 @@
 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.
@@ -606,6 +608,25 @@
     <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
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):