1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
|
==================================
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 :doc:`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:
.. code-block:: none
%include "path/to/base.txt"
whereas in the `PackageLoader` you would use
.. code-block:: none
%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`:
.. code-block:: none
%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:
.. code-block:: none
%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":
.. code-block:: none
%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)`:
.. code-block::none
Hello, Mark!
It was good seeing you last friday. Thanks for the gift!
Sincerely,
Rick
Now we can extend "parent.txt" with "child.txt":
.. code-block:: none
%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:
.. code-block:: none
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
|