http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/docs/intro.rst ---------------------------------------------------------------------- diff --git a/ambari-common/src/main/python/ambari_jinja2/docs/intro.rst b/ambari-common/src/main/python/ambari_jinja2/docs/intro.rst new file mode 100644 index 0000000..912bd39 --- /dev/null +++ b/ambari-common/src/main/python/ambari_jinja2/docs/intro.rst @@ -0,0 +1,168 @@ +Introduction +============ + +This is the documentation for the Jinja2 general purpose templating language. +Jinja2 is a library for Python 2.4 and onwards that is designed to be flexible, +fast and secure. + +If you have any exposure to other text-based template languages, such as Smarty or +Django, you should feel right at home with Jinja2. It's both designer and +developer friendly by sticking to Python's principles and adding functionality +useful for templating environments. + +The key-features are... + +- ... **configurable syntax**. If you are generating LaTeX or other formats + with Jinja2 you can change the delimiters to something that integrates better + into the LaTeX markup. + +- ... **fast**. While performance is not the primarily target of Jinja2 it's + surprisingly fast. The overhead compared to regular Python code was reduced + to the very minimum. + +- ... **easy to debug**. Jinja2 integrates directly into the python traceback + system which allows you to debug Jinja2 templates with regular python + debugging helpers. + +- ... **secure**. It's possible to evaluate untrusted template code if the + optional sandbox is enabled. This allows Jinja2 to be used as templating + language for applications where users may modify the template design. + + +Prerequisites +------------- + +Jinja2 needs at least **Python 2.4** to run. Additionally a working C-compiler +that can create python extensions should be installed for the debugger if you +are using Python 2.4. + +If you don't have a working C-compiler and you are trying to install the source +release with the debugsupport you will get a compiler error. + +.. _ctypes: http://python.net/crew/theller/ctypes/ + + +Installation +------------ + +You have multiple ways to install Jinja2. If you are unsure what to do, go +with the Python egg or tarball. + +As a Python egg (via easy_install) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can install the most recent Jinja2 version using `easy_install`_ or `pip`_:: + + easy_install Jinja2 + pip install Jinja2 + +This will install a Jinja2 egg in your Python installation's site-packages +directory. + +(If you are installing from the windows command line omit the `sudo` and make +sure to run the command as user with administrator rights) + +From the tarball release +~~~~~~~~~~~~~~~~~~~~~~~~~ + +1. Download the most recent tarball from the `download page`_ +2. Unpack the tarball +3. ``sudo python setup.py install`` + +Note that you either have to have setuptools or `distribute`_ installed, +the latter is preferred. + +This will install Jinja2 into your Python installation's site-packages directory. + +.. _distribute: http://pypi.python.org/pypi/distribute + +Installing the development version +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +1. Install `git`_ +2. ``git clone git://github.com/mitsuhiko/ambari_jinja2.git`` +3. ``cd ambari_jinja2`` +4. ``ln -s ambari_jinja2 /usr/lib/python2.X/site-packages`` + +As an alternative to steps 4 you can also do ``python setup.py develop`` +which will install the package via distribute in development mode. This also +has the advantage that the C extensions are compiled. + +.. _download page: http://pypi.python.org/pypi/Jinja2 +.. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools +.. _easy_install: http://peak.telecommunity.com/DevCenter/EasyInstall +.. _pip: http://pypi.python.org/pypi/pip +.. _git: http://git-scm.org/ + + +More Speed with MarkupSafe +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As of version 2.5.1 Jinja2 will check for an installed `MarkupSafe`_ +module. If it can find it, it will use the Markup class of that module +instead of the one that comes with Jinja2. `MarkupSafe` replaces the +older speedups module that came with Jinja2 and has the advantage that is +has a better setup script and will automatically attempt to install the C +version and nicely fall back to a pure Python implementation if that is +not possible. + +The C implementation of MarkupSafe is much faster and recommended when +using Jinja2 with autoescaping. + +.. _MarkupSafe: http://pypi.python.org/pypi/MarkupSafe + + +Enable the debug support Module +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default Jinja2 will not compile the debug support module. Enabling this +will fail if you don't have the Python headers or a working compiler. This +is often the case if you are installing Jinja2 from a windows machine. + +Because the debug support is only necessary for Python 2.4 you will not +have to do this unless you run 2.4:: + + sudo python setup.py --with-debugsupport install + + +Basic API Usage +--------------- + +This section gives you a brief introduction to the Python API for Jinja2 +templates. + +The most basic way to create a template and render it is through +:class:`~ambari_jinja2.Template`. This however is not the recommended way to +work with it if your templates are not loaded from strings but the file +system or another data source: + +>>> from ambari_jinja2 import Template +>>> template = Template('Hello {{ name }}!') +>>> template.render(name='John Doe') +u'Hello John Doe!' + +By creating an instance of :class:`~ambari_jinja2.Template` you get back a new template +object that provides a method called :meth:`~ambari_jinja2.Template.render` which when +called with a dict or keyword arguments expands the template. The dict +or keywords arguments passed to the template are the so-called "context" +of the template. + +What you can see here is that Jinja2 is using unicode internally and the +return value is an unicode string. So make sure that your application is +indeed using unicode internally. + + +Experimental Python 3 Support +----------------------------- + +Jinja 2.3 brings experimental support for Python 3. It means that all +unittests pass on the new version, but there might still be small bugs in +there and behavior might be inconsistent. If you notice any bugs, please +provide feedback in the `Jinja bug tracker`_. + +Also please keep in mind that the documentation is written with Python 2 +in mind, you will have to adapt the shown code examples to Python 3 syntax +for yourself. + + +.. _Jinja bug tracker: http://github.com/mitsuhiko/ambari_jinja2/issues
http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/docs/jinjaext.py ---------------------------------------------------------------------- diff --git a/ambari-common/src/main/python/ambari_jinja2/docs/jinjaext.py b/ambari-common/src/main/python/ambari_jinja2/docs/jinjaext.py new file mode 100644 index 0000000..da95354 --- /dev/null +++ b/ambari-common/src/main/python/ambari_jinja2/docs/jinjaext.py @@ -0,0 +1,192 @@ +# -*- coding: utf-8 -*- +""" + Jinja Documentation Extensions + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Support for automatically documenting filters and tests. + + :copyright: Copyright 2008 by Armin Ronacher. + :license: BSD. +""" +import os +import re +import inspect +import ambari_jinja2 +from itertools import islice +from types import BuiltinFunctionType +from docutils import nodes +from docutils.statemachine import ViewList +from sphinx.ext.autodoc import prepare_docstring +from sphinx.application import TemplateBridge +from pygments.style import Style +from pygments.token import Keyword, Name, Comment, String, Error, \ + Number, Operator, Generic +from ambari_jinja2 import Environment, FileSystemLoader + + +def parse_rst(state, content_offset, doc): + node = nodes.section() + # hack around title style bookkeeping + surrounding_title_styles = state.memo.title_styles + surrounding_section_level = state.memo.section_level + state.memo.title_styles = [] + state.memo.section_level = 0 + state.nested_parse(doc, content_offset, node, match_titles=1) + state.memo.title_styles = surrounding_title_styles + state.memo.section_level = surrounding_section_level + return node.children + + +class JinjaStyle(Style): + title = 'Jinja Style' + default_style = "" + styles = { + Comment: 'italic #aaaaaa', + Comment.Preproc: 'noitalic #B11414', + Comment.Special: 'italic #505050', + + Keyword: 'bold #B80000', + Keyword.Type: '#808080', + + Operator.Word: 'bold #B80000', + + Name.Builtin: '#333333', + Name.Function: '#333333', + Name.Class: 'bold #333333', + Name.Namespace: 'bold #333333', + Name.Entity: 'bold #363636', + Name.Attribute: '#686868', + Name.Tag: 'bold #686868', + Name.Decorator: '#686868', + + String: '#AA891C', + Number: '#444444', + + Generic.Heading: 'bold #000080', + Generic.Subheading: 'bold #800080', + Generic.Deleted: '#aa0000', + Generic.Inserted: '#00aa00', + Generic.Error: '#aa0000', + Generic.Emph: 'italic', + Generic.Strong: 'bold', + Generic.Prompt: '#555555', + Generic.Output: '#888888', + Generic.Traceback: '#aa0000', + + Error: '#F00 bg:#FAA' + } + + +_sig_re = re.compile(r'^[a-zA-Z_][a-zA-Z0-9_]*(\(.*?\))') + + +def format_function(name, aliases, func): + lines = inspect.getdoc(func).splitlines() + signature = '()' + if isinstance(func, BuiltinFunctionType): + match = _sig_re.match(lines[0]) + if match is not None: + del lines[:1 + bool(lines and not lines[0])] + signature = match.group(1) + else: + try: + argspec = inspect.getargspec(func) + if getattr(func, 'environmentfilter', False) or \ + getattr(func, 'contextfilter', False): + del argspec[0][0] + signature = inspect.formatargspec(*argspec) + except: + pass + result = ['.. function:: %s%s' % (name, signature), ''] + result.extend(' ' + line for line in lines) + if aliases: + result.extend(('', ' :aliases: %s' % ', '.join( + '``%s``' % x for x in sorted(aliases)))) + return result + + +def dump_functions(mapping): + def directive(dirname, arguments, options, content, lineno, + content_offset, block_text, state, state_machine): + reverse_mapping = {} + for name, func in mapping.iteritems(): + reverse_mapping.setdefault(func, []).append(name) + filters = [] + for func, names in reverse_mapping.iteritems(): + aliases = sorted(names, key=lambda x: len(x)) + name = aliases.pop() + filters.append((name, aliases, func)) + filters.sort() + + result = ViewList() + for name, aliases, func in filters: + for item in format_function(name, aliases, func): + result.append(item, '<jinjaext>') + + node = nodes.paragraph() + state.nested_parse(result, content_offset, node) + return node.children + return directive + + +from ambari_jinja2.defaults import DEFAULT_FILTERS, DEFAULT_TESTS +jinja_filters = dump_functions(DEFAULT_FILTERS) +jinja_tests = dump_functions(DEFAULT_TESTS) + + +def jinja_nodes(dirname, arguments, options, content, lineno, + content_offset, block_text, state, state_machine): + from ambari_jinja2.nodes import Node + doc = ViewList() + def walk(node, indent): + p = ' ' * indent + sig = ', '.join(node.fields) + doc.append(p + '.. autoclass:: %s(%s)' % (node.__name__, sig), '') + if node.abstract: + members = [] + for key, name in node.__dict__.iteritems(): + if not key.startswith('_') and \ + not hasattr(node.__base__, key) and callable(name): + members.append(key) + if members: + members.sort() + doc.append('%s :members: %s' % (p, ', '.join(members)), '') + if node.__base__ != object: + doc.append('', '') + doc.append('%s :Node type: :class:`%s`' % + (p, node.__base__.__name__), '') + doc.append('', '') + children = node.__subclasses__() + children.sort(key=lambda x: x.__name__.lower()) + for child in children: + walk(child, indent) + walk(Node, 0) + return parse_rst(state, content_offset, doc) + + +def inject_toc(app, doctree, docname): + titleiter = iter(doctree.traverse(nodes.title)) + try: + # skip first title, we are not interested in that one + titleiter.next() + title = titleiter.next() + # and check if there is at least another title + titleiter.next() + except StopIteration: + return + tocnode = nodes.section('') + tocnode['classes'].append('toc') + toctitle = nodes.section('') + toctitle['classes'].append('toctitle') + toctitle.append(nodes.title(text='Table Of Contents')) + tocnode.append(toctitle) + tocnode += doctree.document.settings.env.get_toc_for(docname)[0][1] + title.parent.insert(title.parent.children.index(title), tocnode) + + +def setup(app): + app.add_directive('jinjafilters', jinja_filters, 0, (0, 0, 0)) + app.add_directive('jinjatests', jinja_tests, 0, (0, 0, 0)) + app.add_directive('jinjanodes', jinja_nodes, 0, (0, 0, 0)) + # uncomment for inline toc. links are broken unfortunately + ##app.connect('doctree-resolved', inject_toc) http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/docs/sandbox.rst ---------------------------------------------------------------------- diff --git a/ambari-common/src/main/python/ambari_jinja2/docs/sandbox.rst b/ambari-common/src/main/python/ambari_jinja2/docs/sandbox.rst new file mode 100644 index 0000000..d008408 --- /dev/null +++ b/ambari-common/src/main/python/ambari_jinja2/docs/sandbox.rst @@ -0,0 +1,46 @@ +Sandbox +======= + +The Jinja2 sandbox can be used to evaluate untrusted code. Access to unsafe +attributes and methods is prohibited. + +Assuming `env` is a :class:`SandboxedEnvironment` in the default configuration +the following piece of code shows how it works: + +>>> env.from_string("{{ func.func_code }}").render(func=lambda:None) +u'' +>>> env.from_string("{{ func.func_code.do_something }}").render(func=lambda:None) +Traceback (most recent call last): + ... +SecurityError: access to attribute 'func_code' of 'function' object is unsafe. + + +.. module:: ambari_jinja2.sandbox + +.. autoclass:: SandboxedEnvironment([options]) + :members: is_safe_attribute, is_safe_callable + +.. autoclass:: ImmutableSandboxedEnvironment([options]) + +.. autoexception:: SecurityError + +.. autofunction:: unsafe + +.. autofunction:: is_internal_attribute + +.. autofunction:: modifies_known_mutable + +.. admonition:: Note + + The Jinja2 sandbox alone is no solution for perfect security. Especially + for web applications you have to keep in mind that users may create + templates with arbitrary HTML in so it's crucial to ensure that (if you + are running multiple users on the same server) they can't harm each other + via JavaScript insertions and much more. + + Also the sandbox is only as good as the configuration. We stronly + recommend only passing non-shared resources to the template and use + some sort of whitelisting for attributes. + + Also keep in mind that templates may raise runtime or compile time errors, + so make sure to catch them. http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/docs/switching.rst ---------------------------------------------------------------------- diff --git a/ambari-common/src/main/python/ambari_jinja2/docs/switching.rst b/ambari-common/src/main/python/ambari_jinja2/docs/switching.rst new file mode 100644 index 0000000..ba3cfb1 --- /dev/null +++ b/ambari-common/src/main/python/ambari_jinja2/docs/switching.rst @@ -0,0 +1,242 @@ +Switching from other Template Engines +===================================== + +.. highlight:: html+jinja + +If you have used a different template engine in the past and want to swtich +to Jinja2 here is a small guide that shows the basic syntatic and semantic +changes between some common, similar text template engines for Python. + +Jinja1 +------ + +Jinja2 is mostly compatible with Jinja1 in terms of API usage and template +syntax. The differences between Jinja1 and 2 are explained in the following +list. + +API +~~~ + +Loaders + Jinja2 uses a different loader API. Because the internal representation + of templates changed there is no longer support for external caching + systems such as memcached. The memory consumed by templates is comparable + with regular Python modules now and external caching doesn't give any + advantage. If you have used a custom loader in the past have a look at + the new :ref:`loader API <loaders>`. + +Loading templates from strings + In the past it was possible to generate templates from a string with the + default environment configuration by using `jinja.from_string`. Jinja2 + provides a :class:`Template` class that can be used to do the same, but + with optional additional configuration. + +Automatic unicode conversion + Jinja1 performed automatic conversion of bytestrings in a given encoding + into unicode objects. This conversion is no longer implemented as it + was inconsistent as most libraries are using the regular Python ASCII + bytestring to Unicode conversion. An application powered by Jinja2 + *has to* use unicode internally everywhere or make sure that Jinja2 only + gets unicode strings passed. + +i18n + Jinja1 used custom translators for internationalization. i18n is now + available as Jinja2 extension and uses a simpler, more gettext friendly + interface and has support for babel. For more details see + :ref:`i18n-extension`. + +Internal methods + Jinja1 exposed a few internal methods on the environment object such + as `call_function`, `get_attribute` and others. While they were marked + as being an internal method it was possible to override them. Jinja2 + doesn't have equivalent methods. + +Sandbox + Jinja1 was running sandbox mode by default. Few applications actually + used that feature so it became optional in Jinja2. For more details + about the sandboxed execution see :class:`SandboxedEnvironment`. + +Context + Jinja1 had a stacked context as storage for variables passed to the + environment. In Jinja2 a similar object exists but it doesn't allow + modifications nor is it a singleton. As inheritance is dynamic now + multiple context objects may exist during template evaluation. + +Filters and Tests + Filters and tests are regular functions now. It's no longer necessary + and allowed to use factory functions. + + +Templates +~~~~~~~~~ + +Jinja2 has mostly the same syntax as Jinja1. What's different is that +macros require parentheses around the argument list now. + +Additionally Jinja2 allows dynamic inheritance now and dynamic includes. +The old helper function `rendertemplate` is gone now, `include` can be used +instead. Includes no longer import macros and variable assignments, for +that the new `import` tag is used. This concept is explained in the +:ref:`import` documentation. + +Another small change happened in the `for`-tag. The special loop variable +doesn't have a `parent` attribute, instead you have to alias the loop +yourself. See :ref:`accessing-the-parent-loop` for more details. + + +Django +------ + +If you have previously worked with Django templates, you should find +Jinja2 very familiar. In fact, most of the syntax elements look and +work the same. + +However, Jinja2 provides some more syntax elements covered in the +documentation and some work a bit different. + +This section covers the template changes. As the API is fundamentally +different we won't cover it here. + +Method Calls +~~~~~~~~~~~~ + +In Django method calls work implicitly. With Jinja2 you have to specify that +you want to call an object. Thus this Django code:: + + {% for page in user.get_created_pages %} + ... + {% endfor %} + +will look like this in Jinja:: + + {% for page in user.get_created_pages() %} + ... + {% endfor %} + +This allows you to pass variables to the function which is also used for macros +which is not possible in Django. + +Conditions +~~~~~~~~~~ + +In Django you can use the following constructs to check for equality:: + + {% ifequal foo "bar" %} + ... + {% else %} + ... + {% endifequal %} + +In Jinja2 you can use the normal if statement in combination with operators:: + + {% if foo == 'bar' %} + ... + {% else %} + ... + {% endif %} + +You can also have multiple elif branches in your template:: + + {% if something %} + ... + {% elif otherthing %} + ... + {% elif foothing %} + ... + {% else %} + ... + {% endif %} + +Filter Arguments +~~~~~~~~~~~~~~~~ + +Jinja2 provides more than one argument for filters. Also the syntax for +argument passing is different. A template that looks like this in Django:: + + {{ items|join:", " }} + +looks like this in Jinja2:: + + {{ items|join(', ') }} + +In fact it's a bit more verbose but it allows different types of arguments - +including variables - and more than one of them. + +Tests +~~~~~ + +In addition to filters there also are tests you can perform using the is +operator. Here are some examples:: + + {% if user.user_id is odd %} + {{ user.username|e }} is odd + {% else %} + hmm. {{ user.username|e }} looks pretty normal + {% endif %} + +Loops +~~~~~ + +For loops work very similar to Django, the only incompatibility is that in +Jinja2 the special variable for the loop context is called `loop` and not +`forloop` like in Django. + +Cycle +~~~~~ + +The ``{% cycle %}`` tag does not exist in Jinja because of it's implicit +nature. However you can achieve mostly the same by using the `cycle` +method on a loop object. + +The following Django template:: + + {% for user in users %} + <li class="{% cycle 'odd' 'even' %}">{{ user }}</li> + {% endfor %} + +Would look like this in Jinja:: + + {% for user in users %} + <li class="{{ loop.cycle('odd', 'even') }}">{{ user }}</li> + {% endfor %} + +There is no equivalent of ``{% cycle ... as variable %}``. + + +Mako +---- + +.. highlight:: html+mako + +If you have used Mako so far and want to switch to Jinja2 you can configure +Jinja2 to look more like Mako: + +.. sourcecode:: python + + env = Environment('<%', '%>', '${', '}', '%') + +Once the environment is configure like that Jinja2 should be able to interpret +a small subset of Mako templates. Jinja2 does not support embedded Python code +so you would have to move that out of the template. The syntax for defs (in +Jinja2 defs are called macros) and template inheritance is different too. The +following Mako template:: + + <%inherit file="layout.html" /> + <%def name="title()">Page Title</%def> + <ul> + % for item in list: + <li>${item}</li> + % endfor + </ul> + +Looks like this in Jinja2 with the above configuration:: + + <% extends "layout.html" %> + <% block title %>Page Title<% endblock %> + <% block body %> + <ul> + % for item in list: + <li>${item}</li> + % endfor + </ul> + <% endblock %> http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/docs/templates.rst ---------------------------------------------------------------------- diff --git a/ambari-common/src/main/python/ambari_jinja2/docs/templates.rst b/ambari-common/src/main/python/ambari_jinja2/docs/templates.rst new file mode 100644 index 0000000..4a1f6ff --- /dev/null +++ b/ambari-common/src/main/python/ambari_jinja2/docs/templates.rst @@ -0,0 +1,1365 @@ +Template Designer Documentation +=============================== + +.. highlight:: html+jinja + +This document describes the syntax and semantics of the template engine and +will be most useful as reference to those creating Jinja templates. As the +template engine is very flexible the configuration from the application might +be slightly different from here in terms of delimiters and behavior of +undefined values. + + +Synopsis +-------- + +A template is simply a text file. It can generate any text-based format +(HTML, XML, CSV, LaTeX, etc.). It doesn't have a specific extension, +``.html`` or ``.xml`` are just fine. + +A template contains **variables** or **expressions**, which get replaced with +values when the template is evaluated, and tags, which control the logic of +the template. The template syntax is heavily inspired by Django and Python. + +Below is a minimal template that illustrates a few basics. We will cover +the details later in that document:: + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> + <html lang="en"> + <head> + <title>My Webpage</title> + </head> + <body> + <ul id="navigation"> + {% for item in navigation %} + <li><a href="{{ item.href }}">{{ item.caption }}</a></li> + {% endfor %} + </ul> + + <h1>My Webpage</h1> + {{ a_variable }} + </body> + </html> + +This covers the default settings. The application developer might have +changed the syntax from ``{% foo %}`` to ``<% foo %>`` or something similar. + +There are two kinds of delimiers. ``{% ... %}`` and ``{{ ... }}``. The first +one is used to execute statements such as for-loops or assign values, the +latter prints the result of the expression to the template. + +.. _variables: + +Variables +--------- + +The application passes variables to the templates you can mess around in the +template. Variables may have attributes or elements on them you can access +too. How a variable looks like, heavily depends on the application providing +those. + +You can use a dot (``.``) to access attributes of a variable, alternative the +so-called "subscript" syntax (``[]``) can be used. The following lines do +the same:: + + {{ foo.bar }} + {{ foo['bar'] }} + +It's important to know that the curly braces are *not* part of the variable +but the print statement. If you access variables inside tags don't put the +braces around. + +If a variable or attribute does not exist you will get back an undefined +value. What you can do with that kind of value depends on the application +configuration, the default behavior is that it evaluates to an empty string +if printed and that you can iterate over it, but every other operation fails. + +.. _notes-on-subscriptions: + +.. admonition:: Implementation + + For convenience sake ``foo.bar`` in Jinja2 does the following things on + the Python layer: + + - check if there is an attribute called `bar` on `foo`. + - if there is not, check if there is an item ``'bar'`` in `foo`. + - if there is not, return an undefined object. + + ``foo['bar']`` on the other hand works mostly the same with the a small + difference in the order: + + - check if there is an item ``'bar'`` in `foo`. + - if there is not, check if there is an attribute called `bar` on `foo`. + - if there is not, return an undefined object. + + This is important if an object has an item or attribute with the same + name. Additionally there is the :func:`attr` filter that just looks up + attributes. + +.. _filters: + +Filters +------- + +Variables can by modified by **filters**. Filters are separated from the +variable by a pipe symbol (``|``) and may have optional arguments in +parentheses. Multiple filters can be chained. The output of one filter is +applied to the next. + +``{{ name|striptags|title }}`` for example will remove all HTML Tags from the +`name` and title-cases it. Filters that accept arguments have parentheses +around the arguments, like a function call. This example will join a list +by commas: ``{{ list|join(', ') }}``. + +The :ref:`builtin-filters` below describes all the builtin filters. + +.. _tests: + +Tests +----- + +Beside filters there are also so called "tests" available. Tests can be used +to test a variable against a common expression. To test a variable or +expression you add `is` plus the name of the test after the variable. For +example to find out if a variable is defined you can do ``name is defined`` +which will then return true or false depending on if `name` is defined. + +Tests can accept arguments too. If the test only takes one argument you can +leave out the parentheses to group them. For example the following two +expressions do the same:: + + {% if loop.index is divisibleby 3 %} + {% if loop.index is divisibleby(3) %} + +The :ref:`builtin-tests` below describes all the builtin tests. + + +Comments +-------- + +To comment-out part of a line in a template, use the comment syntax which is +by default set to ``{# ... #}``. This is useful to comment out parts of the +template for debugging or to add information for other template designers or +yourself:: + + {# note: disabled template because we no longer use this + {% for user in users %} + ... + {% endfor %} + #} + + +Whitespace Control +------------------ + +In the default configuration whitespace is not further modified by the +template engine, so each whitespace (spaces, tabs, newlines etc.) is returned +unchanged. If the application configures Jinja to `trim_blocks` the first +newline after a a template tag is removed automatically (like in PHP). + +But you can also strip whitespace in templates by hand. If you put an minus +sign (``-``) to the start or end of an block (for example a for tag), a +comment or variable expression you can remove the whitespaces after or before +that block:: + + {% for item in seq -%} + {{ item }} + {%- endfor %} + +This will yield all elements without whitespace between them. If `seq` was +a list of numbers from ``1`` to ``9`` the output would be ``123456789``. + +If :ref:`line-statements` are enabled they strip leading whitespace +automatically up to the beginning of the line. + +.. admonition:: Note + + You must not use a whitespace between the tag and the minus sign. + + **valid**:: + + {%- if foo -%}...{% endif %} + + **invalid**:: + + {% - if foo - %}...{% endif %} + + +Escaping +-------- + +It is sometimes desirable or even necessary to have Jinja ignore parts it +would otherwise handle as variables or blocks. For example if the default +syntax is used and you want to use ``{{`` as raw string in the template and +not start a variable you have to use a trick. + +The easiest way is to output the variable delimiter (``{{``) by using a +variable expression:: + + {{ '{{' }} + +For bigger sections it makes sense to mark a block `raw`. For example to +put Jinja syntax as example into a template you can use this snippet:: + + {% raw %} + <ul> + {% for item in seq %} + <li>{{ item }}</li> + {% endfor %} + </ul> + {% endraw %} + + +.. _line-statements: + +Line Statements +--------------- + +If line statements are enabled by the application it's possible to mark a +line as a statement. For example if the line statement prefix is configured +to ``#`` the following two examples are equivalent:: + + <ul> + # for item in seq + <li>{{ item }}</li> + # endfor + </ul> + + <ul> + {% for item in seq %} + <li>{{ item }}</li> + {% endfor %} + </ul> + +The line statement prefix can appear anywhere on the line as long as no text +precedes it. For better readability statements that start a block (such as +`for`, `if`, `elif` etc.) may end with a colon:: + + # for item in seq: + ... + # endfor + + +.. admonition:: Note + + Line statements can span multiple lines if there are open parentheses, + braces or brackets:: + + <ul> + # for href, caption in [('index.html', 'Index'), + ('about.html', 'About')]: + <li><a href="{{ href }}">{{ caption }}</a></li> + # endfor + </ul> + +Since Jinja 2.2 line-based comments are available as well. For example if +the line-comment prefix is configured to be ``##`` everything from ``##`` to +the end of the line is ignored (excluding the newline sign):: + + # for item in seq: + <li>{{ item }}</li> ## this comment is ignored + # endfor + + +.. _template-inheritance: + +Template Inheritance +-------------------- + +The most powerful part of Jinja is template inheritance. Template inheritance +allows you to build a base "skeleton" template that contains all the common +elements of your site and defines **blocks** that child templates can override. + +Sounds complicated but is very basic. It's easiest to understand it by starting +with an example. + + +Base Template +~~~~~~~~~~~~~ + +This template, which we'll call ``base.html``, defines a simple HTML skeleton +document that you might use for a simple two-column page. It's the job of +"child" templates to fill the empty blocks with content:: + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> + <html lang="en"> + <html xmlns="http://www.w3.org/1999/xhtml"> + <head> + {% block head %} + <link rel="stylesheet" href="style.css" /> + <title>{% block title %}{% endblock %} - My Webpage</title> + {% endblock %} + </head> + <body> + <div id="content">{% block content %}{% endblock %}</div> + <div id="footer"> + {% block footer %} + © Copyright 2008 by <a href="http://domain.invalid/">you</a>. + {% endblock %} + </div> + </body> + +In this example, the ``{% block %}`` tags define four blocks that child templates +can fill in. All the `block` tag does is to tell the template engine that a +child template may override those portions of the template. + +Child Template +~~~~~~~~~~~~~~ + +A child template might look like this:: + + {% extends "base.html" %} + {% block title %}Index{% endblock %} + {% block head %} + {{ super() }} + <style type="text/css"> + .important { color: #336699; } + </style> + {% endblock %} + {% block content %} + <h1>Index</h1> + <p class="important"> + Welcome on my awesome homepage. + </p> + {% endblock %} + +The ``{% extends %}`` tag is the key here. It tells the template engine that +this template "extends" another template. When the template system evaluates +this template, first it locates the parent. The extends tag should be the +first tag in the template. Everything before it is printed out normally and +may cause confusion. For details about this behavior and how to take +advantage of it, see :ref:`null-master-fallback`. + +The filename of the template depends on the template loader. For example the +:class:`FileSystemLoader` allows you to access other templates by giving the +filename. You can access templates in subdirectories with an slash:: + + {% extends "layout/default.html" %} + +But this behavior can depend on the application embedding Jinja. Note that +since the child template doesn't define the ``footer`` block, the value from +the parent template is used instead. + +You can't define multiple ``{% block %}`` tags with the same name in the +same template. This limitation exists because a block tag works in "both" +directions. That is, a block tag doesn't just provide a hole to fill - it +also defines the content that fills the hole in the *parent*. If there +were two similarly-named ``{% block %}`` tags in a template, that template's +parent wouldn't know which one of the blocks' content to use. + +If you want to print a block multiple times you can however use the special +`self` variable and call the block with that name:: + + <title>{% block title %}{% endblock %}</title> + <h1>{{ self.title() }}</h1> + {% block body %}{% endblock %} + + +Super Blocks +~~~~~~~~~~~~ + +It's possible to render the contents of the parent block by calling `super`. +This gives back the results of the parent block:: + + {% block sidebar %} + <h3>Table Of Contents</h3> + ... + {{ super() }} + {% endblock %} + + +Named Block End-Tags +~~~~~~~~~~~~~~~~~~~~ + +Jinja2 allows you to put the name of the block after the end tag for better +readability:: + + {% block sidebar %} + {% block inner_sidebar %} + ... + {% endblock inner_sidebar %} + {% endblock sidebar %} + +However the name after the `endblock` word must match the block name. + + +Block Nesting and Scope +~~~~~~~~~~~~~~~~~~~~~~~ + +Blocks can be nested for more complex layouts. However per default blocks +may not access variables from outer scopes:: + + {% for item in seq %} + <li>{% block loop_item %}{{ item }}{% endblock %}</li> + {% endfor %} + +This example would output empty ``<li>`` items because `item` is unavailable +inside the block. The reason for this is that if the block is replaced by +a child template a variable would appear that was not defined in the block or +passed to the context. + +Starting with Jinja 2.2 you can explicitly specify that variables are +available in a block by setting the block to "scoped" by adding the `scoped` +modifier to a block declaration:: + + {% for item in seq %} + <li>{% block loop_item scoped %}{{ item }}{% endblock %}</li> + {% endfor %} + +When overriding a block the `scoped` modifier does not have to be provided. + + +Template Objects +~~~~~~~~~~~~~~~~ + +.. versionchanged:: 2.4 + +If a template object was passed to the template context you can +extend from that object as well. Assuming the calling code passes +a layout template as `layout_template` to the environment, this +code works:: + + {% extends layout_template %} + +Previously the `layout_template` variable had to be a string with +the layout template's filename for this to work. + + +HTML Escaping +------------- + +When generating HTML from templates, there's always a risk that a variable will +include characters that affect the resulting HTML. There are two approaches: +manually escaping each variable or automatically escaping everything by default. + +Jinja supports both, but what is used depends on the application configuration. +The default configuaration is no automatic escaping for various reasons: + +- escaping everything except of safe values will also mean that Jinja is + escaping variables known to not include HTML such as numbers which is + a huge performance hit. + +- The information about the safety of a variable is very fragile. It could + happen that by coercing safe and unsafe values the return value is double + escaped HTML. + +Working with Manual Escaping +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If manual escaping is enabled it's **your** responsibility to escape +variables if needed. What to escape? If you have a variable that *may* +include any of the following chars (``>``, ``<``, ``&``, or ``"``) you +**have to** escape it unless the variable contains well-formed and trusted +HTML. Escaping works by piping the variable through the ``|e`` filter: +``{{ user.username|e }}``. + +Working with Automatic Escaping +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When automatic escaping is enabled everything is escaped by default except +for values explicitly marked as safe. Those can either be marked by the +application or in the template by using the `|safe` filter. The main +problem with this approach is that Python itself doesn't have the concept +of tainted values so the information if a value is safe or unsafe can get +lost. If the information is lost escaping will take place which means that +you could end up with double escaped contents. + +Double escaping is easy to avoid however, just rely on the tools Jinja2 +provides and don't use builtin Python constructs such as the string modulo +operator. + +Functions returning template data (macros, `super`, `self.BLOCKNAME`) return +safe markup always. + +String literals in templates with automatic escaping are considered unsafe +too. The reason for this is that the safe string is an extension to Python +and not every library will work properly with it. + + +List of Control Structures +-------------------------- + +A control structure refers to all those things that control the flow of a +program - conditionals (i.e. if/elif/else), for-loops, as well as things like +macros and blocks. Control structures appear inside ``{% ... %}`` blocks +in the default syntax. + +For +~~~ + +Loop over each item in a sequence. For example, to display a list of users +provided in a variable called `users`:: + + <h1>Members</h1> + <ul> + {% for user in users %} + <li>{{ user.username|e }}</li> + {% endfor %} + </ul> + +Inside of a for loop block you can access some special variables: + ++-----------------------+---------------------------------------------------+ +| Variable | Description | ++=======================+===================================================+ +| `loop.index` | The current iteration of the loop. (1 indexed) | ++-----------------------+---------------------------------------------------+ +| `loop.index0` | The current iteration of the loop. (0 indexed) | ++-----------------------+---------------------------------------------------+ +| `loop.revindex` | The number of iterations from the end of the loop | +| | (1 indexed) | ++-----------------------+---------------------------------------------------+ +| `loop.revindex0` | The number of iterations from the end of the loop | +| | (0 indexed) | ++-----------------------+---------------------------------------------------+ +| `loop.first` | True if first iteration. | ++-----------------------+---------------------------------------------------+ +| `loop.last` | True if last iteration. | ++-----------------------+---------------------------------------------------+ +| `loop.length` | The number of items in the sequence. | ++-----------------------+---------------------------------------------------+ +| `loop.cycle` | A helper function to cycle between a list of | +| | sequences. See the explanation below. | ++-----------------------+---------------------------------------------------+ + +Within a for-loop, it's possible to cycle among a list of strings/variables +each time through the loop by using the special `loop.cycle` helper:: + + {% for row in rows %} + <li class="{{ loop.cycle('odd', 'even') }}">{{ row }}</li> + {% endfor %} + +With Jinja 2.1 an extra `cycle` helper exists that allows loop-unbound +cycling. For more information have a look at the :ref:`builtin-globals`. + +.. _loop-filtering: + +Unlike in Python it's not possible to `break` or `continue` in a loop. You +can however filter the sequence during iteration which allows you to skip +items. The following example skips all the users which are hidden:: + + {% for user in users if not user.hidden %} + <li>{{ user.username|e }}</li> + {% endfor %} + +The advantage is that the special `loop` variable will count correctly thus +not counting the users not iterated over. + +If no iteration took place because the sequence was empty or the filtering +removed all the items from the sequence you can render a replacement block +by using `else`:: + + <ul> + {% for user in users %} + <li>{{ user.username|e }}</li> + {% else %} + <li><em>no users found</em></li> + {% endfor %} + </ul> + +It is also possible to use loops recursively. This is useful if you are +dealing with recursive data such as sitemaps. To use loops recursively you +basically have to add the `recursive` modifier to the loop definition and +call the `loop` variable with the new iterable where you want to recurse. + +The following example implements a sitemap with recursive loops:: + + <ul class="sitemap"> + {%- for item in sitemap recursive %} + <li><a href="{{ item.href|e }}">{{ item.title }}</a> + {%- if item.children -%} + <ul class="submenu">{{ loop(item.children) }}</ul> + {%- endif %}</li> + {%- endfor %} + </ul> + + +If +~~ + +The `if` statement in Jinja is comparable with the if statements of Python. +In the simplest form you can use it to test if a variable is defined, not +empty or not false:: + + {% if users %} + <ul> + {% for user in users %} + <li>{{ user.username|e }}</li> + {% endfor %} + </ul> + {% endif %} + +For multiple branches `elif` and `else` can be used like in Python. You can +use more complex :ref:`expressions` there too:: + + {% if kenny.sick %} + Kenny is sick. + {% elif kenny.dead %} + You killed Kenny! You bastard!!! + {% else %} + Kenny looks okay --- so far + {% endif %} + +If can also be used as :ref:`inline expression <if-expression>` and for +:ref:`loop filtering <loop-filtering>`. + + +Macros +~~~~~~ + +Macros are comparable with functions in regular programming languages. They +are useful to put often used idioms into reusable functions to not repeat +yourself. + +Here a small example of a macro that renders a form element:: + + {% macro input(name, value='', type='text', size=20) -%} + <input type="{{ type }}" name="{{ name }}" value="{{ + value|e }}" size="{{ size }}"> + {%- endmacro %} + +The macro can then be called like a function in the namespace:: + + <p>{{ input('username') }}</p> + <p>{{ input('password', type='password') }}</p> + +If the macro was defined in a different template you have to +:ref:`import <import>` it first. + +Inside macros you have access to three special variables: + +`varargs` + If more positional arguments are passed to the macro than accepted by the + macro they end up in the special `varargs` variable as list of values. + +`kwargs` + Like `varargs` but for keyword arguments. All unconsumed keyword + arguments are stored in this special variable. + +`caller` + If the macro was called from a :ref:`call<call>` tag the caller is stored + in this variable as macro which can be called. + +Macros also expose some of their internal details. The following attributes +are available on a macro object: + +`name` + The name of the macro. ``{{ input.name }}`` will print ``input``. + +`arguments` + A tuple of the names of arguments the macro accepts. + +`defaults` + A tuple of default values. + +`catch_kwargs` + This is `true` if the macro accepts extra keyword arguments (ie: accesses + the special `kwargs` variable). + +`catch_varargs` + This is `true` if the macro accepts extra positional arguments (ie: + accesses the special `varargs` variable). + +`caller` + This is `true` if the macro accesses the special `caller` variable and may + be called from a :ref:`call<call>` tag. + +If a macro name starts with an underscore it's not exported and can't +be imported. + + +.. _call: + +Call +~~~~ + +In some cases it can be useful to pass a macro to another macro. For this +purpose you can use the special `call` block. The following example shows +a macro that takes advantage of the call functionality and how it can be +used:: + + {% macro render_dialog(title, class='dialog') -%} + <div class="{{ class }}"> + <h2>{{ title }}</h2> + <div class="contents"> + {{ caller() }} + </div> + </div> + {%- endmacro %} + + {% call render_dialog('Hello World') %} + This is a simple dialog rendered by using a macro and + a call block. + {% endcall %} + +It's also possible to pass arguments back to the call block. This makes it +useful as replacement for loops. Generally speaking a call block works +exactly like an macro, just that it doesn't have a name. + +Here an example of how a call block can be used with arguments:: + + {% macro dump_users(users) -%} + <ul> + {%- for user in users %} + <li><p>{{ user.username|e }}</p>{{ caller(user) }}</li> + {%- endfor %} + </ul> + {%- endmacro %} + + {% call(user) dump_users(list_of_user) %} + <dl> + <dl>Realname</dl> + <dd>{{ user.realname|e }}</dd> + <dl>Description</dl> + <dd>{{ user.description }}</dd> + </dl> + {% endcall %} + + +Filters +~~~~~~~ + +Filter sections allow you to apply regular Jinja2 filters on a block of +template data. Just wrap the code in the special `filter` section:: + + {% filter upper %} + This text becomes uppercase + {% endfilter %} + + +Assignments +~~~~~~~~~~~ + +Inside code blocks you can also assign values to variables. Assignments at +top level (outside of blocks, macros or loops) are exported from the template +like top level macros and can be imported by other templates. + +Assignments use the `set` tag and can have multiple targets:: + + {% set navigation = [('index.html', 'Index'), ('about.html', 'About')] %} + {% set key, value = call_something() %} + + +Extends +~~~~~~~ + +The `extends` tag can be used to extend a template from another one. You +can have multiple of them in a file but only one of them may be executed +at the time. See the section about :ref:`template-inheritance` above. + + +Block +~~~~~ + +Blocks are used for inheritance and act as placeholders and replacements +at the same time. They are documented in detail as part of the section +about :ref:`template-inheritance`. + + +Include +~~~~~~~ + +The `include` statement is useful to include a template and return the +rendered contents of that file into the current namespace:: + + {% include 'header.html' %} + Body + {% include 'footer.html' %} + +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 %} + +.. versionadded:: 2.2 + +You can also provide a list of templates that are checked for existence +before inclusion. The first template that exists will be included. If +`ignore missing` is given, it will fall back to rendering nothing if +none of the templates exist, otherwise it will raise an exception. + +Example:: + + {% include ['page_detailed.html', 'page.html'] %} + {% include ['special_sidebar.html', 'sidebar.html'] ignore missing %} + +.. versionchanged:: 2.4 + If a template object was passed to the template context you can + include that object using `include`. + +.. _import: + +Import +~~~~~~ + +Jinja2 supports putting often used code into macros. These macros can go into +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 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. + +Imagine we have a helper module that renders forms (called `forms.html`):: + + {% macro input(name, value='', type='text') -%} + <input type="{{ type }}" value="{{ value|e }}" name="{{ name }}"> + {%- endmacro %} + + {%- macro textarea(name, value='', rows=10, cols=40) -%} + <textarea name="{{ name }}" rows="{{ rows }}" cols="{{ cols + }}">{{ value|e }}</textarea> + {%- endmacro %} + +The easiest and most flexible is importing the whole module into a variable. +That way you can access the attributes:: + + {% import 'forms.html' as forms %} + <dl> + <dt>Username</dt> + <dd>{{ forms.input('username') }}</dd> + <dt>Password</dt> + <dd>{{ forms.input('password', type='password') }}</dd> + </dl> + <p>{{ forms.textarea('comment') }}</p> + + +Alternatively you can import names from the template into the current +namespace:: + + {% from 'forms.html' import input as input_field, textarea %} + <dl> + <dt>Username</dt> + <dd>{{ input_field('username') }}</dd> + <dt>Password</dt> + <dd>{{ input_field('password', type='password') }}</dd> + </dl> + <p>{{ textarea('comment') }}</p> + +Macros and variables starting with one ore more underscores are private and +cannot be imported. + +.. versionchanged:: 2.4 + If a template object was passed to the template context you can + import from that object. + + +.. _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 %} + +.. admonition:: Note + + In Jinja 2.0 the context that was passed to the included template + did not include variables defined in the template. As a matter of + fact this did not work:: + + {% for box in boxes %} + {% include "render_box.html" %} + {% endfor %} + + The included template ``render_box.html`` is not able to access + `box` in Jinja 2.0, but in Jinja 2.1. + + +.. _expressions: + +Expressions +----------- + +Jinja allows basic expressions everywhere. These work very similar to regular +Python and even if you're not working with Python you should feel comfortable +with it. + +Literals +~~~~~~~~ + +The simplest form of expressions are literals. Literals are representations +for Python objects such as strings and numbers. The following literals exist: + +"Hello World": + Everything between two double or single quotes is a string. They are + useful whenever you need a string in the template (for example as + arguments to function calls, filters or just to extend or include a + template). + +42 / 42.23: + Integers and floating point numbers are created by just writing the + number down. If a dot is present the number is a float, otherwise an + integer. Keep in mind that for Python ``42`` and ``42.0`` is something + different. + +['list', 'of', 'objects']: + Everything between two brackets is a list. Lists are useful to store + sequential data in or to iterate over them. For example you can easily + create a list of links using lists and tuples with a for loop:: + + <ul> + {% for href, caption in [('index.html', 'Index'), ('about.html', 'About'), + ('downloads.html', 'Downloads')] %} + <li><a href="{{ href }}">{{ caption }}</a></li> + {% endfor %} + </ul> + +('tuple', 'of', 'values'): + Tuples are like lists, just that you can't modify them. If the tuple + only has one item you have to end it with a comma. Tuples are usually + used to represent items of two or more elements. See the example above + for more details. + +{'dict': 'of', 'key': 'and', 'value': 'pairs'}: + A dict in Python is a structure that combines keys and values. Keys must + be unique and always have exactly one value. Dicts are rarely used in + templates, they are useful in some rare cases such as the :func:`xmlattr` + filter. + +true / false: + true is always true and false is always false. + +.. admonition:: Note + + The special constants `true`, `false` and `none` are indeed lowercase. + Because that caused confusion in the past, when writing `True` expands + to an undefined variable that is considered false, all three of them can + be written in title case too (`True`, `False`, and `None`). However for + consistency (all Jinja identifiers are lowercase) you should use the + lowercase versions. + +Math +~~~~ + +Jinja allows you to calculate with values. This is rarely useful in templates +but exists for completeness' sake. The following operators are supported: + +\+ + Adds two objects together. Usually the objects are numbers but if both are + strings or lists you can concatenate them this way. This however is not + the preferred way to concatenate strings! For string concatenation have + a look at the ``~`` operator. ``{{ 1 + 1 }}`` is ``2``. + +\- + Substract the second number from the first one. ``{{ 3 - 2 }}`` is ``1``. + +/ + Divide two numbers. The return value will be a floating point number. + ``{{ 1 / 2 }}`` is ``{{ 0.5 }}``. + +// + Divide two numbers and return the truncated integer result. + ``{{ 20 / 7 }}`` is ``2``. + +% + Calculate the remainder of an integer division. ``{{ 11 % 7 }}`` is ``4``. + +\* + Multiply the left operand with the right one. ``{{ 2 * 2 }}`` would + return ``4``. This can also be used to repeat a string multiple times. + ``{{ '=' * 80 }}`` would print a bar of 80 equal signs. + +\** + Raise the left operand to the power of the right operand. ``{{ 2**3 }}`` + would return ``8``. + +Comparisons +~~~~~~~~~~~ + +== + Compares two objects for equality. + +!= + Compares two objects for inequality. + +> + `true` if the left hand side is greater than the right hand side. + +>= + `true` if the left hand side is greater or equal to the right hand side. + +< + `true` if the left hand side is lower than the right hand side. + +<= + `true` if the left hand side is lower or equal to the right hand side. + +Logic +~~~~~ + +For `if` statements, `for` filtering or `if` expressions it can be useful to +combine multiple expressions: + +and + Return true if the left and the right operand is true. + +or + Return true if the left or the right operand is true. + +not + negate a statement (see below). + +(expr) + group an expression. + +.. admonition:: Note + + The ``is`` and ``in`` operators support negation using an infix notation + too: ``foo is not bar`` and ``foo not in bar`` instead of ``not foo is bar`` + and ``not foo in bar``. All other expressions require a prefix notation: + ``not (foo and bar).`` + + +Other Operators +~~~~~~~~~~~~~~~ + +The following operators are very useful but don't fit into any of the other +two categories: + +in + Perform sequence / mapping containment test. Returns true if the left + operand is contained in the right. ``{{ 1 in [1, 2, 3] }}`` would for + example return true. + +is + Performs a :ref:`test <tests>`. + +\| + Applies a :ref:`filter <filters>`. + +~ + Converts all operands into strings and concatenates them. + ``{{ "Hello " ~ name ~ "!" }}`` would return (assuming `name` is + ``'John'``) ``Hello John!``. + +() + Call a callable: ``{{ post.render() }}``. Inside of the parentheses you + can use positional arguments and keyword arguments like in python: + ``{{ post.render(user, full=true) }}``. + +. / [] + Get an attribute of an object. (See :ref:`variables`) + + +.. _if-expression: + +If Expression +~~~~~~~~~~~~~ + +It is also possible to use inline `if` expressions. These are useful in some +situations. For example you can use this to extend from one template if a +variable is defined, otherwise from the default layout template:: + + {% extends layout_template if layout_template is defined else 'master.html' %} + +The general syntax is ``<do something> if <something is true> else <do +something else>``. + +The `else` part is optional. If not provided the else block implicitly +evaluates into an undefined object:: + + {{ '[%s]' % page.title if page.title }} + + +.. _builtin-filters: + +List of Builtin Filters +----------------------- + +.. jinjafilters:: + + +.. _builtin-tests: + +List of Builtin Tests +--------------------- + +.. jinjatests:: + +.. _builtin-globals: + +List of Global Functions +------------------------ + +The following functions are available in the global scope by default: + +.. function:: range([start,] stop[, step]) + + Return a list containing an arithmetic progression of integers. + range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0. + When step is given, it specifies the increment (or decrement). + For example, range(4) returns [0, 1, 2, 3]. The end point is omitted! + These are exactly the valid indices for a list of 4 elements. + + This is useful to repeat a template block multiple times for example + to fill a list. Imagine you have 7 users in the list but you want to + render three empty items to enforce a height with CSS:: + + <ul> + {% for user in users %} + <li>{{ user.username }}</li> + {% endfor %} + {% for number in range(10 - users|count) %} + <li class="empty"><span>...</span></li> + {% endfor %} + </ul> + +.. function:: lipsum(n=5, html=True, min=20, max=100) + + Generates some lorem ipsum for the template. Per default five paragraphs + with HTML are generated each paragraph between 20 and 100 words. If html + is disabled regular text is returned. This is useful to generate simple + contents for layout testing. + +.. function:: dict(\**items) + + A convenient alternative to dict literals. ``{'foo': 'bar'}`` is the same + as ``dict(foo='bar')``. + +.. class:: cycler(\*items) + + The cycler allows you to cycle among values similar to how `loop.cycle` + works. Unlike `loop.cycle` however you can use this cycler outside of + loops or over multiple loops. + + This is for example very useful if you want to show a list of folders and + files, with the folders on top, but both in the same list with alternating + row colors. + + The following example shows how `cycler` can be used:: + + {% set row_class = cycler('odd', 'even') %} + <ul class="browser"> + {% for folder in folders %} + <li class="folder {{ row_class.next() }}">{{ folder|e }}</li> + {% endfor %} + {% for filename in files %} + <li class="file {{ row_class.next() }}">{{ filename|e }}</li> + {% endfor %} + </ul> + + A cycler has the following attributes and methods: + + .. method:: reset() + + Resets the cycle to the first item. + + .. method:: next() + + Goes one item a head and returns the then current item. + + .. attribute:: current + + Returns the current item. + + **new in Jinja 2.1** + +.. class:: joiner(sep=', ') + + A tiny helper that can be use to "join" multiple sections. A joiner is + passed a string and will return that string every time it's calld, except + the first time in which situation it returns an empty string. You can + use this to join things:: + + {% set pipe = joiner("|") %} + {% if categories %} {{ pipe() }} + Categories: {{ categories|join(", ") }} + {% endif %} + {% if author %} {{ pipe() }} + Author: {{ author() }} + {% endif %} + {% if can_edit %} {{ pipe() }} + <a href="?action=edit">Edit</a> + {% endif %} + + **new in Jinja 2.1** + + +Extensions +---------- + +The following sections cover the built-in Jinja2 extensions that may be +enabled by the application. The application could also provide further +extensions not covered by this documentation. In that case there should +be a separate document explaining the extensions. + +.. _i18n-in-templates: + +i18n +~~~~ + +If the i18n extension is enabled it's possible to mark parts in the template +as translatable. To mark a section as translatable you can use `trans`:: + + <p>{% trans %}Hello {{ user }}!{% endtrans %}</p> + +To translate a template expression --- say, using template filters or just +accessing an attribute of an object --- you need to bind the expression to a +name for use within the translation block:: + + <p>{% trans user=user.username %}Hello {{ user }}!{% endtrans %}</p> + +If you need to bind more than one expression inside a `trans` tag, separate +the pieces with a comma (``,``):: + + {% trans book_title=book.title, author=author.name %} + This is {{ book_title }} by {{ author }} + {% endtrans %} + +Inside trans tags no statements are allowed, only variable tags are. + +To pluralize, specify both the singular and plural forms with the `pluralize` +tag, which appears between `trans` and `endtrans`:: + + {% trans count=list|length %} + There is {{ count }} {{ name }} object. + {% pluralize %} + There are {{ count }} {{ name }} objects. + {% endtrans %} + +Per default the first variable in a block is used to determine the correct +singular or plural form. If that doesn't work out you can specify the name +which should be used for pluralizing by adding it as parameter to `pluralize`:: + + {% trans ..., user_count=users|length %}... + {% pluralize user_count %}...{% endtrans %} + +It's also possible to translate strings in expressions. For that purpose +three functions exist: + +_ `gettext`: translate a single string +- `ngettext`: translate a pluralizable string +- `_`: alias for `gettext` + +For example you can print a translated string easily this way:: + + {{ _('Hello World!') }} + +To use placeholders you can use the `format` filter:: + + {{ _('Hello %(user)s!')|format(user=user.username) }} + +For multiple placeholders always use keyword arguments to `format` as other +languages may not use the words in the same order. + +.. versionchanged:: 2.5 + +If newstyle gettext calls are activated (:ref:`newstyle-gettext`), using +placeholders is a lot easier: + +.. sourcecode:: html+jinja + + {{ gettext('Hello World!') }} + {{ gettext('Hello %(name)s!', name='World') }} + {{ ngettext('%(num)d apple', '%(num)d apples', apples|count) }} + +Note that the `ngettext` function's format string automatically recieves +the count as `num` parameter additionally to the regular parameters. + + +Expression Statement +~~~~~~~~~~~~~~~~~~~~ + +If the expression-statement extension is loaded a tag called `do` is available +that works exactly like the regular variable expression (``{{ ... }}``) just +that it doesn't print anything. This can be used to modify lists:: + + {% do navigation.append('a string') %} + + +Loop Controls +~~~~~~~~~~~~~ + +If the application enables the :ref:`loopcontrols-extension` it's possible to +use `break` and `continue` in loops. When `break` is reached, the loop is +terminated, if `continue` is eached the processing is stopped and continues +with the next iteration. + +Here a loop that skips every second item:: + + {% for user in users %} + {%- if loop.index is even %}{% continue %}{% endif %} + ... + {% endfor %} + +Likewise a look that stops processing after the 10th iteration:: + + {% for user in users %} + {%- if loop.index >= 10 %}{% break %}{% endif %} + {%- endfor %} + + +With Statement +~~~~~~~~~~~~~~ + +.. versionadded:: 2.3 + +If the application enables the :ref:`with-extension` it is possible to +use the `with` keyword in templates. This makes it possible to create +a new inner scope. Variables set within this scope are not visible +outside of the scope. + +With in a nutshell:: + + {% with %} + {% set foo = 42 %} + {{ foo }} foo is 42 here + {% endwith %} + foo is not visible here any longer + +Because it is common to set variables at the beginning of the scope +you can do that within the with statement. The following two examples +are equivalent:: + + {% with foo = 42 %} + {{ foo }} + {% endwith %} + + {% with %} + {% set foo = 42 %} + {{ foo }} + {% endwith %} + +.. _autoescape-overrides: + +Autoescape Extension +-------------------- + +.. versionadded:: 2.4 + +If the application enables the :ref:`autoescape-extension` one can +activate and deactivate the autoescaping from within the templates. + +Example:: + + {% autoescape true %} + Autoescaping is active within this block + {% endautoescape %} + + {% autoescape false %} + Autoescaping is inactive within this block + {% endautoescape %} + +After the `endautoescape` the behavior is reverted to what it was before. http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/docs/tricks.rst ---------------------------------------------------------------------- diff --git a/ambari-common/src/main/python/ambari_jinja2/docs/tricks.rst b/ambari-common/src/main/python/ambari_jinja2/docs/tricks.rst new file mode 100644 index 0000000..566575e --- /dev/null +++ b/ambari-common/src/main/python/ambari_jinja2/docs/tricks.rst @@ -0,0 +1,100 @@ +Tips and Tricks +=============== + +.. highlight:: html+jinja + +This part of the documentation shows some tips and tricks for Jinja2 +templates. + + +.. _null-master-fallback: + +Null-Master Fallback +-------------------- + +Jinja2 supports dynamic inheritance and does not distinguish between parent +and child template as long as no `extends` tag is visited. While this leads +to the surprising behavior that everything before the first `extends` tag +including whitespace is printed out instead of being igored, it can be used +for a neat trick. + +Usually child templates extend from one template that adds a basic HTML +skeleton. However it's possible put the `extends` tag into an `if` tag to +only extend from the layout template if the `standalone` variable evaluates +to false which it does per default if it's not defined. Additionally a very +basic skeleton is added to the file so that if it's indeed rendered with +`standalone` set to `True` a very basic HTML skeleton is added:: + + {% if not standalone %}{% extends 'master.html' %}{% endif -%} + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> + <title>{% block title %}The Page Title{% endblock %}</title> + <link rel="stylesheet" href="style.css" type="text/css"> + {% block body %} + <p>This is the page body.</p> + {% endblock %} + + +Alternating Rows +---------------- + +If you want to have different styles for each row of a table or +list you can use the `cycle` method on the `loop` object:: + + <ul> + {% for row in rows %} + <li class="{{ loop.cycle('odd', 'even') }}">{{ row }}</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. + + +Highlighting Active Menu Items +------------------------------ + +Often you want to have a navigation bar with an active navigation +item. This is really simple to achieve. Because assignments outside +of `block`\s in child templates are global and executed before the layout +template is evaluated it's possible to define the active menu item in the +child template:: + + {% extends "layout.html" %} + {% set active_page = "index" %} + +The layout template can then access `active_page`. Additionally it makes +sense to defined a default for that variable:: + + {% set navigation_bar = [ + ('/', 'index', 'Index'), + ('/downloads/', 'downloads', 'Downloads'), + ('/about/', 'about', 'About') + ] -%} + {% set active_page = active_page|default('index') -%} + ... + <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> + ... + +.. _accessing-the-parent-loop: + +Accessing the parent Loop +------------------------- + +The special `loop` variable always points to the innermost loop. If it's +desired to have access to an outer loop it's possible to alias it:: + + <table> + {% for row in table %} + <tr> + {% set rowloop = loop %} + {% for cell in row %} + <td id="cell-{{ rowloop.index }}-{{ loop.index }}>{{ cell }}</td> + {% endfor %} + </tr> + {% endfor %} + </table> http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/examples/basic/cycle.py ---------------------------------------------------------------------- diff --git a/ambari-common/src/main/python/ambari_jinja2/examples/basic/cycle.py b/ambari-common/src/main/python/ambari_jinja2/examples/basic/cycle.py new file mode 100644 index 0000000..7ca9683 --- /dev/null +++ b/ambari-common/src/main/python/ambari_jinja2/examples/basic/cycle.py @@ -0,0 +1,13 @@ +from ambari_jinja2 import Environment + + +env = Environment(line_statement_prefix="#", variable_start_string="${", variable_end_string="}") + + +print env.from_string("""\ +<ul> +# for item in range(10) + <li class="${loop.cycle('odd', 'even')}">${item}</li> +# endfor +</ul>\ +""").render() http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/examples/basic/debugger.py ---------------------------------------------------------------------- diff --git a/ambari-common/src/main/python/ambari_jinja2/examples/basic/debugger.py b/ambari-common/src/main/python/ambari_jinja2/examples/basic/debugger.py new file mode 100644 index 0000000..96b8f36 --- /dev/null +++ b/ambari-common/src/main/python/ambari_jinja2/examples/basic/debugger.py @@ -0,0 +1,7 @@ +from ambari_jinja2 import Environment +from ambari_jinja2.loaders import FileSystemLoader + +env = Environment(loader=FileSystemLoader('templates')) + +tmpl = env.get_template('broken.html') +print tmpl.render(seq=[3, 2, 4, 5, 3, 2, 0, 2, 1]) http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/examples/basic/inheritance.py ---------------------------------------------------------------------- diff --git a/ambari-common/src/main/python/ambari_jinja2/examples/basic/inheritance.py b/ambari-common/src/main/python/ambari_jinja2/examples/basic/inheritance.py new file mode 100644 index 0000000..10ea11c --- /dev/null +++ b/ambari-common/src/main/python/ambari_jinja2/examples/basic/inheritance.py @@ -0,0 +1,12 @@ +from ambari_jinja2 import Environment +from ambari_jinja2.loaders import DictLoader + + +env = Environment(loader=DictLoader({ +'a': '''[A[{% block body %}{% endblock %}]]''', +'b': '''{% extends 'a' %}{% block body %}[B]{% endblock %}''', +'c': '''{% extends 'b' %}{% block body %}###{{ super() }}###{% endblock %}''' +})) + + +print env.get_template('c').render() http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/examples/basic/templates/broken.html ---------------------------------------------------------------------- diff --git a/ambari-common/src/main/python/ambari_jinja2/examples/basic/templates/broken.html b/ambari-common/src/main/python/ambari_jinja2/examples/basic/templates/broken.html new file mode 100644 index 0000000..294d5c9 --- /dev/null +++ b/ambari-common/src/main/python/ambari_jinja2/examples/basic/templates/broken.html @@ -0,0 +1,6 @@ +{% from 'subbroken.html' import may_break %} +<ul> +{% for item in seq %} + <li>{{ may_break(item) }}</li> +{% endfor %} +</ul> http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/examples/basic/templates/subbroken.html ---------------------------------------------------------------------- diff --git a/ambari-common/src/main/python/ambari_jinja2/examples/basic/templates/subbroken.html b/ambari-common/src/main/python/ambari_jinja2/examples/basic/templates/subbroken.html new file mode 100644 index 0000000..245eb7e --- /dev/null +++ b/ambari-common/src/main/python/ambari_jinja2/examples/basic/templates/subbroken.html @@ -0,0 +1,3 @@ +{% macro may_break(item) -%} + [{{ item / 0 }}] +{%- endmacro %} http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/examples/basic/test.py ---------------------------------------------------------------------- diff --git a/ambari-common/src/main/python/ambari_jinja2/examples/basic/test.py b/ambari-common/src/main/python/ambari_jinja2/examples/basic/test.py new file mode 100644 index 0000000..721c960 --- /dev/null +++ b/ambari-common/src/main/python/ambari_jinja2/examples/basic/test.py @@ -0,0 +1,27 @@ +from ambari_jinja2 import Environment +from ambari_jinja2.loaders import DictLoader + +env = Environment(loader=DictLoader({ +'child.html': u'''\ +{% extends master_layout or 'master.html' %} +{% include helpers = 'helpers.html' %} +{% macro get_the_answer() %}42{% endmacro %} +{% title = 'Hello World' %} +{% block body %} + {{ get_the_answer() }} + {{ helpers.conspirate() }} +{% endblock %} +''', +'master.html': u'''\ +<!doctype html> +<title>{{ title }}</title> +{% block body %}{% endblock %} +''', +'helpers.html': u'''\ +{% macro conspirate() %}23{% endmacro %} +''' +})) + + +tmpl = env.get_template("child.html") +print tmpl.render() http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/examples/basic/test_filter_and_linestatements.py ---------------------------------------------------------------------- diff --git a/ambari-common/src/main/python/ambari_jinja2/examples/basic/test_filter_and_linestatements.py b/ambari-common/src/main/python/ambari_jinja2/examples/basic/test_filter_and_linestatements.py new file mode 100644 index 0000000..975f74c --- /dev/null +++ b/ambari-common/src/main/python/ambari_jinja2/examples/basic/test_filter_and_linestatements.py @@ -0,0 +1,25 @@ +from ambari_jinja2 import Environment + + +env = Environment(line_statement_prefix='%', variable_start_string="${", variable_end_string="}") +tmpl = env.from_string("""\ +% macro foo() + ${caller(42)} +% endmacro +<ul> +% for item in seq + <li>${item}</li> +% endfor +</ul> +% call(var) foo() + [${var}] +% endcall +% filter escape + <hello world> + % for item in [1, 2, 3] + - ${item} + % endfor +% endfilter +""") + +print tmpl.render(seq=range(10)) http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/examples/basic/test_loop_filter.py ---------------------------------------------------------------------- diff --git a/ambari-common/src/main/python/ambari_jinja2/examples/basic/test_loop_filter.py b/ambari-common/src/main/python/ambari_jinja2/examples/basic/test_loop_filter.py new file mode 100644 index 0000000..051ca42 --- /dev/null +++ b/ambari-common/src/main/python/ambari_jinja2/examples/basic/test_loop_filter.py @@ -0,0 +1,12 @@ +from ambari_jinja2 import Environment + +tmpl = Environment().from_string("""\ +<ul> +{%- for item in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] if item % 2 == 0 %} + <li>{{ loop.index }} / {{ loop.length }}: {{ item }}</li> +{%- endfor %} +</ul> +if condition: {{ 1 if foo else 0 }} +""") + +print tmpl.render(foo=True) http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/examples/basic/translate.py ---------------------------------------------------------------------- diff --git a/ambari-common/src/main/python/ambari_jinja2/examples/basic/translate.py b/ambari-common/src/main/python/ambari_jinja2/examples/basic/translate.py new file mode 100644 index 0000000..7bada71 --- /dev/null +++ b/ambari-common/src/main/python/ambari_jinja2/examples/basic/translate.py @@ -0,0 +1,6 @@ +from ambari_jinja2 import Environment + +print Environment(extensions=['ambari_jinja2.i18n.TransExtension']).from_string("""\ +{% trans %}Hello {{ user }}!{% endtrans %} +{% trans count=users|count %}{{ count }} user{% pluralize %}{{ count }} users{% endtrans %} +""").render(user="someone")