available in all blocks below. This allows you to use `include` statements to
load often used macros at once.
+Undefined Variables
+===================
+
+If you have already worked with python you probably know about the fact that
+undefined variables raise an exception. This is different in Jinja. There is a
+special value called `undefined` that represents values that do not exist.
+
+This special variable works complete different from any variables you maybe
+know. If you print it using ``{{ variable }}`` it will not appear because it's
+literally empty. If you try to iterate over it, it will work. But no items
+are returned. Comparing this value to any other value results in `false`.
+Even if you compare it to itself:
+
+.. sourcecode:: jinja
+
+ {{ undefined == undefined }}
+ will return false. Not even undefined is undefined :)
+ Use `is defined` / `is not defined`:
+
+ {{ undefined is not defined }}
+ will return true.
+
+There are also some additional rules regarding this special value. Any
+mathematical operators (``+``, ``-``, ``*``, ``/``) return the operand
+as result:
+
+.. sourcecode:: jinja
+
+ {{ undefined + "foo" }}
+ returns "foo"
+
+ {{ undefined - 42 }}
+ returns 42. Note: not -42!
+
+In any expression `undefined` evaluates to `false`. It has no length, all
+attribute calls return undefined, calling too:
+
+.. sourcecode:: jinja
+
+ {{ undefined.attribute().attribute_too[42] }}
+ still returns `undefined`.
+
Internationalization
====================
- `Syntax Reference <designerdoc.txt>`_
- `Differences To Django <fromdjango.txt>`_
+
+ - `Recipies <recipies.txt>`_
--- /dev/null
+========
+Recipies
+========
+
+Here are some typical recipes for the usage of Jinja templates.
+
+Alternating Rows
+================
+
+If you want to have different styles for each row of a table or
+list you can use the ``cycle`` tag:
+
+.. sourcecode:: html+jinja
+
+ <ul>
+ {% for row in sequence %}
+ <li class="{% cycle 'even', 'odd' %}">{{ row|e }}</li>
+ {% endfor %}
+ </ul>
+
+``cycle`` can take an unlimited amount of strings. Each time this
+tag is encountered the next item from the list is rendered.
+If you pass it just one argument it's meant to be a sequence.
+
+Active Menu Item
+================
+
+Often you want to have a navigation bar with an active navigation
+item. This is really simple to achieve. Because ``set`` tags outside
+of ``blocks`` are global you can do something like this:
+
+**layout.html**:
+
+ .. sourcecode:: html+jinja
+
+ {% set navigation_bar = [
+ ('/', 'index', 'Index'),
+ ('/downloads/', 'downloads', 'Downloads'),
+ ('/about/', 'about', 'About')
+ ] %}
+ ...
+ <ul id="navigation">
+ {% for href, id, caption in navigation_bar %}
+ <li{% if id == active_page %} class="active"{% endif
+ %}><a href="{{ href|e }}">{{ caption|e }}</a>/li>
+ {% endfor %}
+ </ul>
+ ...
+
+**index.html**:
+
+ .. sourcecode:: jinja
+
+ {% extends "layout.html" %}
+ {% set active_page = "index" %}
from jinja.exceptions import TemplateRuntimeError
+def contextcallable(f):
+ """
+ Mark a function context callable.
+ """
+ f.jinja_context_callable = True
+ return f
+
+
+def unsafe(f):
+ """
+ Mark function as unsafe.
+ """
+ f.jinja_unsafe_call = True
+ return f
+
+
class UndefinedType(object):
"""
An object that does not exist.
return 0
def __float__(self):
- return 1
+ return 0.0
+
+ def __eq__(self, other):
+ return False
+
+ def __ne__(self, other):
+ return True
def __call__(self, *args, **kwargs):
return self
# cache object used for filters and tests
self.cache = {}
- def get_translator(self):
- """Return the translator for i18n."""
- return FakeTranslator()
-
def pop(self):
"""Pop the last layer from the stack and return it."""
rv = self._stack.pop()
"""
from jinja.filters import FILTERS as DEFAULT_FILTERS
from jinja.tests import TESTS as DEFAULT_TESTS
-from jinja.utils import debug_context
+from jinja.utils import debug_context, safe_range
DEFAULT_NAMESPACE = {
- 'range': range,
+ 'range': safe_range,
'debug': debug_context
}
from jinja.lexer import Lexer
from jinja.parser import Parser
from jinja.loaders import LoaderWrapper
-from jinja.datastructure import Undefined, Context, Markup
+from jinja.datastructure import Undefined, Context, Markup, FakeTranslator
from jinja.utils import escape
from jinja.exceptions import FilterNotFound, TestNotFound, SecurityException
from jinja.defaults import DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE
except UnicodeError:
return str(value).decode(self.charset, 'ignore')
+ def get_translator(self, context):
+ """
+ Return the translator for i18n.
+
+ A translator is an object that provides the two functions
+ ``gettext(string)`` and ``ngettext(singular, plural, n)``. Note
+ that both of them have to return unicode!
+ """
+ return FakeTranslator()
+
def apply_filters(self, value, context, filters):
"""
Apply a list of filters on the variable.
node = getattr(node, name)
return node
- def call_function(self, f, args, kwargs, dyn_args, dyn_kwargs):
+ def call_function(self, f, context, args, kwargs, dyn_args, dyn_kwargs):
"""
Function call helper. Called for all functions that are passed
any arguments.
if getattr(f, 'jinja_unsafe_call', False) or \
getattr(f, 'alters_data', False):
raise SecurityException('unsafe function %r called' % f.__name__)
+ if getattr(f, 'jinja_context_callable', False):
+ args = (self, context) + args
return f(*args, **kwargs)
- def call_function_simple(self, f):
+ def call_function_simple(self, f, context):
"""
- Function call without arguments.
+ Function call without arguments. Because of the smaller signature and
+ fewer logic here we have a bit of redundant code.
"""
if getattr(f, 'jinja_unsafe_call', False) or \
getattr(f, 'alters_data', False):
raise SecurityException('unsafe function %r called' % f.__name__)
+ if getattr(f, 'jinja_context_callable', False):
+ return f(self, context)
return f()
def finish_var(self, value):
# add translation helpers if required
if self.require_translations:
lines.append(
- ' translator = context.get_translator()\n'
+ ' translator = environment.get_translator(context)\n'
' def translate(s, p=None, n=None, r=None):\n'
' if p is None:\n'
' return translator.gettext(s) % (r or {})\n'
else:
args.append(self.handle_node(arg))
if not (args or kwargs or star_args or dstar_args):
- return 'call_function_simple(%s)' % self.handle_node(node.node)
- return 'call_function(%s, %s, {%s}, %s, %s)' % (
+ return 'call_function_simple(%s, context)' % self.handle_node(node.node)
+ return 'call_function(%s, context, %s, {%s}, %s, %s)' % (
self.handle_node(node.node),
_to_tuple(args),
', '.join(['%r: %s' % i for i in kwargs.iteritems()]),
except ImportError:
deque = None
+#: number of maximal range items
+MAX_RANGE = 1000000
+
_debug_info_re = re.compile(r'^\s*\# DEBUG\(filename=(.*?), lineno=(.*?)\)$')
_escape_pairs = {
queue.extend(node.getChildNodes())
-def debug_context():
+def debug_context(env, context):
"""
Use this function in templates to get a printed context.
- Use this only in templates because it touches the stack.
"""
- context = sys._getframe(2).f_locals['context']
from pprint import pformat
return pformat(context.to_dict())
+debug_context.jinja_context_callable = True
+
+
+def safe_range(start, stop=None, step=None):
+ """
+ "Safe" form of range that does not generate too large lists.
+ """
+ # this also works with None since None is always smaller than
+ # any other value.
+ if start > MAX_RANGE:
+ start = MAX_RANGE
+ if stop > MAX_RANGE:
+ stop = MAX_RANGE
+ if step is None:
+ step = 1
+ if stop is None:
+ return range(0, start, step)
+ return range(start, stop, step)
# python2.4 and lower has a bug regarding joining of broken generators
<a href="http://peak.telecommunity.com/DevCenter/EasyInstall">easy_install</a>,
you can do it using this command:
</p>
- <pre>easy_install Pygments</pre>
+ <pre>easy_install Jinja</pre>
<p>
You can also get the development source from subversion using this command:
</p>
<span class="nt"></ul></span>
<span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
</pre></div>
- <h2>Philosphy</h2>
+ <h2>Philosophy</h2>
<p>
- Application logic is for the controller but don't try to make the live for the
- template designer too hard by giving him too less functionality.
+ Application logic is for the controller but don't try to make the life for the
+ template designer too hard by giving him too few functionality.
</p>
<h2>Features</h2>
<ul>