Include statements can now be marked with ``ignore missing`` to skip
authorArmin Ronacher <armin.ronacher@active-4.com>
Sat, 27 Dec 2008 12:10:38 +0000 (13:10 +0100)
committerArmin Ronacher <armin.ronacher@active-4.com>
Sat, 27 Dec 2008 12:10:38 +0000 (13:10 +0100)
non existing templates.

--HG--
branch : trunk

CHANGES
docs/templates.rst
jinja2/compiler.py
jinja2/nodes.py
jinja2/parser.py
jinja2/runtime.py
tests/test_imports.py

diff --git a/CHANGES b/CHANGES
index a70061b1b9ebd53f8f9db92973c0ca79ea357a2f..ab75555cb5ddf5ecebad33f546e3e6720c0963d6 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,13 @@
 Jinja2 Changelog
 ================
 
+Version 2.2
+-----------
+(codename unknown, release date yet unknown)
+
+- Include statements can now be marked with ``ignore missing`` to skip
+  non existing templates.
+
 Version 2.1.1
 -------------
 (Bugfix release)
index ac80400614a9a17c1adaf4759140e836241540e9..fe21eefe89179fe3edaa6eae03c88712ac14953e 100644 (file)
@@ -724,6 +724,16 @@ 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`.
 
+From Jinja 2.2 onwards you can mark an include with ``ignore missing`` in
+which case Jinja will ignore the statement if the template to be ignored
+does not exist.  When combined with ``with`` or ``without context`` it has
+to be placed *before* the context visibility statement.  Here some valid
+examples::
+
+    {% include "sidebar.html" ignore missing %}
+    {% include "sidebar.html" ignore missing with context %}
+    {% include "sidebar.html" ignore missing without context %}
+
 .. _import:
 
 Import
index 54a80baaa2b5cc207008fe55be219087d82acf1b..e17aa1de80023cff06323340fad221adf9d350b3 100644 (file)
@@ -818,22 +818,35 @@ class CodeGenerator(NodeVisitor):
 
     def visit_Include(self, node, frame):
         """Handles includes."""
+        if node.ignore_missing:
+            self.writeline('try:')
+            self.indent()
+        self.writeline('template = environment.get_template(', node)
+        self.visit(node.template, frame)
+        self.write(', %r)' % self.name)
+        if node.ignore_missing:
+            self.outdent()
+            self.writeline('except TemplateNotFound:')
+            self.indent()
+            self.writeline('pass')
+            self.outdent()
+            self.writeline('else:')
+            self.indent()
+
         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, '
                            'locals())):')
         else:
-            self.writeline('for event in environment.get_template(', node)
-            self.visit(node.template, frame)
-            self.write(', %r).module._body_stream:' %
-                       self.name)
+            self.writeline('for event in template.module._body_stream:')
+
         self.indent()
         self.simple_write('event', frame)
         self.outdent()
 
+        if node.ignore_missing:
+            self.outdent()
+
     def visit_Import(self, node, frame):
         """Visit regular imports."""
         self.writeline('l_%s = ' % node.target, node)
index 405622a9de0b940aa7f7f1d2883d783dc9a58ae3..9d78b254436d94825cc5fb55cdb3d6dc6ba37804 100644 (file)
@@ -274,7 +274,7 @@ class Block(Stmt):
 
 class Include(Stmt):
     """A node that represents the include tag."""
-    fields = ('template', 'with_context')
+    fields = ('template', 'with_context', 'ignore_missing')
 
 
 class Import(Stmt):
index d6f1b3647d96e909033eb358294f1832c07a400e..e8f07c5f58540d7a0b550decd59b89b39436a38a 100644 (file)
@@ -170,6 +170,12 @@ class Parser(object):
     def parse_include(self):
         node = nodes.Include(lineno=self.stream.next().lineno)
         node.template = self.parse_expression()
+        if self.stream.current.test('name:ignore') and \
+           self.stream.look().test('name:missing'):
+            node.ignore_missing = True
+            self.stream.skip(2)
+        else:
+            node.ignore_missing = False
         return self.parse_import_context(node, True)
 
     def parse_import(self):
index 2ed3ac64651c860462dbe82f8cf4907c21dd45ad..87c2354606bfb316494a2ac53c86ea93ea126866 100644 (file)
@@ -12,13 +12,14 @@ import sys
 from itertools import chain, imap
 from jinja2.utils import Markup, partial, soft_unicode, escape, missing, \
      concat, MethodType, FunctionType
-from jinja2.exceptions import UndefinedError, TemplateRuntimeError
+from jinja2.exceptions import UndefinedError, TemplateRuntimeError, \
+     TemplateNotFound
 
 
 # these variables are exported to the template runtime
 __all__ = ['LoopContext', 'Context', 'TemplateReference', 'Macro', 'Markup',
            'TemplateRuntimeError', 'missing', 'concat', 'escape',
-           'markup_join', 'unicode_join']
+           'markup_join', 'unicode_join', 'TemplateNotFound']
 
 
 #: the types we support for context functions
index bf8f56979d65d0294c5a9433688d0b6329da0e7f..224441c5a1c739ad3c53ceaad8a96eb73ba4c59e 100644 (file)
@@ -6,7 +6,9 @@
     :copyright: 2007 by Armin Ronacher.
     :license: BSD, see LICENSE for more details.
 """
+from py.test import raises
 from jinja2 import Environment, DictLoader
+from jinja2.exceptions import TemplateNotFound
 
 
 test_env = Environment(loader=DictLoader(dict(
@@ -40,6 +42,15 @@ def test_context_include():
     assert t.render(foo=42) == '[|23]'
 
 
+def test_include_ignoring_missing():
+    t = test_env.from_string('{% include "missing" %}')
+    raises(TemplateNotFound, t.render)
+    for extra in '', 'with context', 'without context':
+        t = test_env.from_string('{% include "missing" ignore missing ' +
+                                 extra + ' %}')
+        assert t.render() == ''
+
+
 def test_context_include_with_overrides():
     env = Environment(loader=DictLoader(dict(
         main="{% for item in [1, 2, 3] %}{% include 'item' %}{% endfor %}",