Kajiki Text Templates¶
Kajiki provides a full-featured text template engine in addition to the XML templating engine for cases where you don’t want to necessarily generate markup. This document describes that language. Templates are text files that include template directives that control how the template is rendered and expressions that are substituted into the generated text at render time.
Please see Kajiki Templating Basics for general information on embedding Python code in templates.
Basic Expressions¶
Let’s start with a hello world template:
>>> import kajiki
>>> Template = kajiki.TextTemplate('Hello, $name!')
>>> print(Template(dict(name='world')).render())
Hello, world!
By default, the $-syntax picks up any identifiers following it, as well as any periods. If you want something more explicit, use the extended expression form as follows:
>>> Template = kajiki.TextTemplate('Hello, 2+2 is ${2+2}')
>>> print(Template().render())
Hello, 2+2 is 4
If you wish to include a literal $, simply double it:
>>> Template = kajiki.TextTemplate('The price is $$${price}')
>>> print(Template(dict(price='5.00')).render())
The price is $5.00
Control Flow¶
Kajiki provides several directives that affect the rendering of a template. This section describes the various directives. Directives in text templates can either be enclosed by {% … %} characters or they can exist on a line by themselves prefixed by a %. Template directives must always be terminated by an ‘end’ directive (either {%end%} or %end.
Note
Whitespace can sometimes be tricky in text templates. Kajiki provides a bit of help in managing it. First, if you wish to break a line without having the newline included in the generated text, simply end the line with a backslash (). Kajiki will also remove any whitespace before a tag that begins with the delimiter {%-. Directives that appear on their own line via the % prefix never appear in the output, and neither they do not generate any whitespace.
%if, %else¶
Only render the enclosed content if the expression evaluates to a truthy value:
>>> Template = kajiki.TextTemplate('{%if foo %}bar{%else%}baz{%end%}')
>>> print(Template(dict(foo=True)).render())
bar
>>> print(Template(dict(foo=False)).render())
baz
%switch, %case, %else¶
Perform multiple tests to render one of several alternatives. The first matching case is rendered, and if no case matches, the else branch is rendered:
>>> Template = kajiki.TextTemplate('''$i is \
... {%switch i % 2 %}{%case 0%}even{%else%}odd{%end%}''')
>>> print(Template(dict(i=4)).render())
4 is even
>>> print(Template(dict(i=3)).render())
3 is odd
%for¶
Repeatedly render the content for each item in an iterable:
>>> Template = kajiki.TextTemplate('''%for i in range(3)
... $i
... %end''')
>>> print(Template().render(), end='')
0
1
2
%def¶
Defines a function that can be used elsewhere in the template:
>>> Template = kajiki.TextTemplate('''%def evenness(n)
... {%-if n % 2 == 0 %}even{%else%}odd{%end%}\\
... %end
... %for i in range(2)
... $i is ${evenness(i)}
... %end''')
>>> print(Template().render(), end='')
0 is even
1 is odd
%call¶
Call a function, passing a block of template code as a ‘lambda’ parameter. Note that this is a special case of calling when you wish to insert some templated text in the expansion of a function call. In normal circumstances, you would just use ${my_function(args)}.
>>> Template = kajiki.TextTemplate('''%def quote(caller, speaker)
... %for i in range(2)
... Quoth $speaker, "${caller(i)}."
... %end
... %end
... %call(n) quote(%caller, 'the raven')
... Nevermore $n\\
... %end''')
>>> print(Template().render(), end='')
Quoth the raven, "Nevermore 0."
Quoth the raven, "Nevermore 1."
%include¶
Includes the text of another template verbatim. The precise semantics of this tag depend on the TemplateLoader being used, as the TemplateLoader is used to parse the name of the template being included and render its contents into the current template. For instance, with the FileLoader, you might use the following:
%include "path/to/base.txt"
whereas in the PackageLoader you would use
%include package1.package2.base
%import¶
With %import, you can make the functions defined in another template available without expanding the full template in-place. Suppose that we saved the following template in a file lib.txt:
%def evenness(n)
%if n % 2 == 0
even\
%else
odd\
%end
%end
Then (using the FileLoader) we could write a template using the evenness function as follows:
%import "lib.txt" as lib
%for i in range(5)
%i is ${lib.evenness(i)}
%end
Inheritance (%extends, %block)¶
Kajiki supports a concept of inheritance whereby child templates can extend parent templates, replacing their “methods” (functions) and “blocks” (to be defined below). For instance, consider the following template “parent.txt”:
%def greet(name)
Hello, $name!\
%end
%def sign(name)
Sincerely,
$name\
%end
${greet(to)}
%block body
It was good seeing you last Friday. Thanks for the gift!
%end
${sign(from_)}
This would render to the following (assuming a context of dict(to=Mark, from_=Rick):
Now we can extend “parent.txt” with “child.txt”:
%extends "parent.txt"
%def greet(name)
Dear $name:\
%end
%block body
${parent_block()}\
And don't forget you owe me money!
%end
Rendering this template would then give us:
Dear Mark:
It was good seeing you last Friday! Thanks for the gift!
And don't forget you owe me money!
Sincerely,
Rick
Notice how in the child block, we have overridden both the block “body” and the function “greet.” When overriding a block, we always have access to the parent template’s block of the same name via the parent_block() function.
If you ever need to access the parent template itself (perhaps to call another function), kajiki provides access to a special variable in child templates parent. Likewise, if a template is being extended, the variable child is available. Kajiki also provides the special variables local (the template currently being defined) and self (the child-most template of an inheritance chain). The following example illustrates these variables in a 3-level inheritance hierarchy:
>>> parent = kajiki.TextTemplate('''
... %def header()
... # Header name=$name
... %end
... %def footer()
... # Footer
... %end
... %def body()
... ## Parent Body
... id() = ${id()}
... local.id() = ${local.id()}
... self.id() = ${self.id()}
... child.id() = ${child.id()}
... %end
... %def id()
... parent\\
... %end
... ${header()}${body()}${footer()}''')
>>> mid = kajiki.TextTemplate('''%extends "parent.txt"
... %def id()
... mid\\
... %end
... ''')
>>> child = kajiki.TextTemplate('''%extends "mid.txt"
... %def id()
... child\\
... %end
... %def body()
... ## Child Body
... ${parent.body()}\\
... %end
... ''')
>>> loader = kajiki.MockLoader({
... 'parent.txt':parent,
... 'mid.txt':mid,
... 'child.txt':child})
>>> Template = loader.import_('child.txt')
>>> print(Template(dict(name='Rick')).render(), end='')
# Header name=Rick
## Child Body
## Parent Body
id() = child
local.id() = parent
self.id() = child
child.id() = mid
# Footer