From 649c7b9bd0c818329531fc393fbfa8af11a1e2d3 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Wed, 30 May 2007 20:59:06 +0200 Subject: [PATCH] [svn] some minor updates in jinja --HG-- branch : trunk --- docs/src/devintro.txt | 29 ++++++++++++++++++++ jinja/environment.py | 4 +-- jinja/loaders.py | 11 ++++++-- tests/runtime/strange.py | 57 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 96 insertions(+), 5 deletions(-) create mode 100644 tests/runtime/strange.py diff --git a/docs/src/devintro.txt b/docs/src/devintro.txt index 596ab67..0b61924 100644 --- a/docs/src/devintro.txt +++ b/docs/src/devintro.txt @@ -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 ================== diff --git a/jinja/environment.py b/jinja/environment.py index 011e12a..91cd2a5 100644 --- a/jinja/environment.py +++ b/jinja/environment.py @@ -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()) diff --git a/jinja/loaders.py b/jinja/loaders.py index ea1b265..27999a2 100644 --- a/jinja/loaders.py +++ b/jinja/loaders.py @@ -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 index 0000000..56a0852 --- /dev/null +++ b/tests/runtime/strange.py @@ -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?' -- 2.26.2