[svn] some minor updates in jinja
authorArmin Ronacher <armin.ronacher@active-4.com>
Wed, 30 May 2007 18:59:06 +0000 (20:59 +0200)
committerArmin Ronacher <armin.ronacher@active-4.com>
Wed, 30 May 2007 18:59:06 +0000 (20:59 +0200)
--HG--
branch : trunk

docs/src/devintro.txt
jinja/environment.py
jinja/loaders.py
tests/runtime/strange.py [new file with mode: 0644]

index 596ab67b063596efe9029ecff4a2bce64a78000e..0b61924d8319f1243024c414915f4a60298fed3d 100644 (file)
@@ -90,6 +90,35 @@ Theoretically you can provide your own singleton by subclassing
 the basic use cases. The `Undefined` object in that module exists for
 backwards compatibility and is an alias for `SilentUndefined`.
 
+To create your own undefined singleton do something like this:
+
+.. sourcecode:: jinja
+
+    from jinja.datastructure import AbstractUndefinedType, make_undefined
+
+    class MyUndefinedType(AbstractUndefindedType):
+        __slots__ = ()
+
+        def __iter__(self):
+            return iter(int, 0)
+
+        def __reduce__(self):
+            return 'MyUndefined'
+
+    MyUndefined = make_undefined(MyUndefinedType)
+
+The only thing you have to do is to override `__reduce__` so that it returns
+the name of the singleton instance and create the instance using
+`make_undefined`. Everything else is up to you. Note that currently attributes
+on undefined objects are available in the Jinja layer too which however
+will change in one of the next Jinja versions. So if you put a `foo` attribute
+on your undefined singleton you will be able to do ``{{ undefined.foo }}``
+by now but certainly not in the future.
+
+This limitation currently exists because undefined is treated as normal object
+and thus affected by normal getattr calls.
+
+
 Automatic Escaping
 ==================
 
index 011e12a39366683f542ea8a8254a43edb39619e8..91cd2a52fab4f05204fb986d3a6809122e661ff5 100644 (file)
@@ -8,14 +8,13 @@
     :copyright: 2007 by Armin Ronacher.
     :license: BSD, see LICENSE for more details.
 """
-import re
 from jinja.lexer import Lexer
 from jinja.parser import Parser
 from jinja.loaders import LoaderWrapper
 from jinja.datastructure import SilentUndefined, Markup, Context, FakeTranslator
 from jinja.utils import collect_translations, get_attribute
 from jinja.exceptions import FilterNotFound, TestNotFound, \
-     SecurityException, TemplateSyntaxError, TemplateRuntimeError
+     SecurityException, TemplateSyntaxError
 from jinja.defaults import DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE
 
 
@@ -186,7 +185,6 @@ class Environment(object):
         Python code. This code is wrapped within a `Template` class that
         allows you to render it.
         """
-        from jinja.parser import Parser
         from jinja.translators.python import PythonTranslator
         try:
             rv = PythonTranslator.process(self, Parser(self, source).parse())
index ea1b2659b7cccc23ec2919f88c31c372c431d3f2..27999a21a47ba1dae504b69de965c4a80383dcc1 100644 (file)
@@ -42,6 +42,13 @@ def get_cachename(cachepath, name, salt=None):
                              (name, salt or '')).hexdigest())
 
 
+
+def _loader_missing(*args, **kwargs):
+    """Helper function for `LoaderWrapper`."""
+    raise RuntimeError('no loader defined')
+
+
+
 class LoaderWrapper(object):
     """
     Wraps a loader so that it's bound to an environment.
@@ -52,7 +59,7 @@ class LoaderWrapper(object):
         self.environment = environment
         self.loader = loader
         if self.loader is None:
-            self.get_source = self.parse = self.load = self._loader_missing
+            self.get_source = self.parse = self.load = _loader_missing
             self.available = False
         else:
             self.available = True
@@ -97,7 +104,7 @@ class LoaderWrapper(object):
         raise RuntimeError('no loader defined')
 
     def __nonzero__(self):
-        return self.loader is not None
+        return self.available
 
 
 class BaseLoader(object):
diff --git a/tests/runtime/strange.py b/tests/runtime/strange.py
new file mode 100644 (file)
index 0000000..56a0852
--- /dev/null
@@ -0,0 +1,57 @@
+import jdebug
+from jinja import Environment, DictLoader
+
+base_tmpl = """
+{% block content %}Default{% endblock %}
+"""
+
+### condition is inside of block
+
+test1 = """
+{% extends 'base' %}
+
+{% block content %}
+  {% if False %}
+    {{ throw_exception() }}
+  {% endif %}
+{% endblock %}
+"""
+
+### block is inside of condition
+
+test2 = """
+{% extends 'base' %}
+
+{% if False %}
+  {% block content %}
+    {{ throw_exception() }}
+  {% endblock %}
+{% endif %}
+"""
+
+class TestException(Exception):
+    pass
+
+def throw_exception():
+    raise TestException()
+
+env = Environment(
+    loader=DictLoader(dict(base=base_tmpl))
+)
+
+if __name__ == '__main__':
+    for name in 'test1', 'test2':
+        template_body = globals().get(name)
+        template = env.from_string(template_body)
+        try:
+            print 'Rendering template:\n"""%s"""' % template_body
+            template.render(throw_exception=throw_exception)
+        except TestException:
+            print 'Result: throw_exception() was called'
+        else:
+            print 'Result: throw_exception() was not called'
+        print
+
+    print 'First template illustrates that condition is working well'
+    print 'The question is - why {% block %} is being evalueted '\
+          'in false condition in second template?'