| [534] | 1 | .. -*- mode: rst; encoding: utf-8 -*- |
|---|
| 2 | |
|---|
| 3 | ======================== |
|---|
| 4 | Genshi Templating Basics |
|---|
| 5 | ======================== |
|---|
| 6 | |
|---|
| 7 | Genshi provides a template engine that can be used for generating either |
|---|
| 8 | markup (such as HTML_ or XML_) or plain text. While both share some of the |
|---|
| 9 | syntax (and much of the underlying implementation) they are essentially |
|---|
| 10 | separate languages. |
|---|
| 11 | |
|---|
| 12 | .. _html: http://www.w3.org/html/ |
|---|
| 13 | .. _xml: http://www.w3.org/XML/ |
|---|
| 14 | |
|---|
| 15 | This document describes the common parts of the template engine and will be most |
|---|
| 16 | useful as reference to those developing Genshi templates. Templates are XML or |
|---|
| 17 | plain text files that include processing directives_ that affect how the |
|---|
| 18 | template is rendered, and template expressions_ that are dynamically substituted |
|---|
| 19 | by variable data. |
|---|
| 20 | |
|---|
| 21 | |
|---|
| 22 | .. contents:: Contents |
|---|
| 23 | :depth: 3 |
|---|
| 24 | .. sectnum:: |
|---|
| 25 | |
|---|
| 26 | -------- |
|---|
| 27 | Synopsis |
|---|
| 28 | -------- |
|---|
| 29 | |
|---|
| 30 | A Genshi *markup template* is a well-formed XML document with embedded Python |
|---|
| 31 | used for control flow and variable substitution. Markup templates should be |
|---|
| [1098] | 32 | used to generate any kind of HTML or XML output, as they provide a number of |
|---|
| 33 | advantages over simple text-based templates (such as automatic escaping of |
|---|
| 34 | variable data). |
|---|
| [534] | 35 | |
|---|
| [1098] | 36 | The following is a simple Genshi markup template: |
|---|
| [534] | 37 | |
|---|
| [614] | 38 | .. code-block:: genshi |
|---|
| 39 | |
|---|
| [534] | 40 | <?python |
|---|
| 41 | title = "A Genshi Template" |
|---|
| 42 | fruits = ["apple", "orange", "kiwi"] |
|---|
| 43 | ?> |
|---|
| 44 | <html xmlns:py="http://genshi.edgewall.org/"> |
|---|
| 45 | <head> |
|---|
| 46 | <title py:content="title">This is replaced.</title> |
|---|
| 47 | </head> |
|---|
| 48 | |
|---|
| 49 | <body> |
|---|
| 50 | <p>These are some of my favorite fruits:</p> |
|---|
| 51 | <ul> |
|---|
| 52 | <li py:for="fruit in fruits"> |
|---|
| 53 | I like ${fruit}s |
|---|
| 54 | </li> |
|---|
| 55 | </ul> |
|---|
| 56 | </body> |
|---|
| 57 | </html> |
|---|
| 58 | |
|---|
| 59 | This example shows: |
|---|
| 60 | |
|---|
| [1098] | 61 | (a) a Python code block in a processing instruction |
|---|
| [534] | 62 | (b) the Genshi namespace declaration |
|---|
| 63 | (c) usage of templates directives (``py:content`` and ``py:for``) |
|---|
| 64 | (d) an inline Python expression (``${fruit}``). |
|---|
| 65 | |
|---|
| [614] | 66 | The template would generate output similar to this: |
|---|
| [537] | 67 | |
|---|
| [614] | 68 | .. code-block:: genshi |
|---|
| 69 | |
|---|
| [537] | 70 | <html> |
|---|
| 71 | <head> |
|---|
| 72 | <title>A Genshi Template</title> |
|---|
| 73 | </head> |
|---|
| 74 | |
|---|
| 75 | <body> |
|---|
| 76 | <p>These are some of my favorite fruits:</p> |
|---|
| 77 | <ul> |
|---|
| 78 | <li>I like apples</li> |
|---|
| 79 | <li>I like oranges</li> |
|---|
| 80 | <li>I like kiwis</li> |
|---|
| 81 | </ul> |
|---|
| 82 | </body> |
|---|
| 83 | </html> |
|---|
| 84 | |
|---|
| [1098] | 85 | A *text template* is a simple plain text document that can also contain |
|---|
| 86 | embedded Python code. Text templates are intended to be used for simple |
|---|
| 87 | *non-markup* text formats, such as the body of an plain text email. For |
|---|
| 88 | example: |
|---|
| [534] | 89 | |
|---|
| [614] | 90 | .. code-block:: genshitext |
|---|
| 91 | |
|---|
| [534] | 92 | Dear $name, |
|---|
| 93 | |
|---|
| 94 | These are some of my favorite fruits: |
|---|
| 95 | #for fruit in fruits |
|---|
| 96 | * $fruit |
|---|
| 97 | #end |
|---|
| 98 | |
|---|
| 99 | |
|---|
| 100 | ---------- |
|---|
| 101 | Python API |
|---|
| 102 | ---------- |
|---|
| 103 | |
|---|
| 104 | The Python code required for templating with Genshi is generally based on the |
|---|
| 105 | following pattern: |
|---|
| 106 | |
|---|
| 107 | * Attain a ``MarkupTemplate`` or ``TextTemplate`` object from a string or |
|---|
| 108 | file-like object containing the template source. This can either be done |
|---|
| 109 | directly, or through a ``TemplateLoader`` instance. |
|---|
| 110 | * Call the ``generate()`` method of the template, passing any data that should |
|---|
| 111 | be made available to the template as keyword arguments. |
|---|
| 112 | * Serialize the resulting stream using its ``render()`` method. |
|---|
| 113 | |
|---|
| [614] | 114 | For example: |
|---|
| [534] | 115 | |
|---|
| [614] | 116 | .. code-block:: pycon |
|---|
| 117 | |
|---|
| [534] | 118 | >>> from genshi.template import MarkupTemplate |
|---|
| 119 | >>> tmpl = MarkupTemplate('<h1>Hello, $name!</h1>') |
|---|
| 120 | >>> stream = tmpl.generate(name='world') |
|---|
| [1076] | 121 | >>> print(stream.render('xhtml')) |
|---|
| [534] | 122 | <h1>Hello, world!</h1> |
|---|
| 123 | |
|---|
| [706] | 124 | .. note:: See the Serialization_ section of the `Markup Streams`_ page for |
|---|
| 125 | information on configuring template output options. |
|---|
| 126 | |
|---|
| [614] | 127 | Using a text template is similar: |
|---|
| [534] | 128 | |
|---|
| [614] | 129 | .. code-block:: pycon |
|---|
| 130 | |
|---|
| [534] | 131 | >>> from genshi.template import TextTemplate |
|---|
| 132 | >>> tmpl = TextTemplate('Hello, $name!') |
|---|
| 133 | >>> stream = tmpl.generate(name='world') |
|---|
| [1076] | 134 | >>> print(stream) |
|---|
| [534] | 135 | Hello, world! |
|---|
| 136 | |
|---|
| [706] | 137 | .. note:: If you want to use text templates, you should consider using the |
|---|
| 138 | ``NewTextTemplate`` class instead of simply ``TextTemplate``. See |
|---|
| 139 | the `Text Template Language`_ page. |
|---|
| [534] | 140 | |
|---|
| 141 | .. _serialization: streams.html#serialization |
|---|
| [706] | 142 | .. _`Text Template Language`: text-templates.html |
|---|
| [534] | 143 | .. _`Markup Streams`: streams.html |
|---|
| 144 | |
|---|
| [1107] | 145 | Using a `template loader`_ provides the advantage that “compiled” templates are |
|---|
| [534] | 146 | automatically cached, and only parsed again when the template file changes. In |
|---|
| 147 | addition, it enables the use of a *template search path*, allowing template |
|---|
| 148 | directories to be spread across different file-system locations. Using a |
|---|
| [614] | 149 | template loader would generally look as follows: |
|---|
| [534] | 150 | |
|---|
| [614] | 151 | .. code-block:: python |
|---|
| 152 | |
|---|
| [534] | 153 | from genshi.template import TemplateLoader |
|---|
| 154 | loader = TemplateLoader([templates_dir1, templates_dir2]) |
|---|
| 155 | tmpl = loader.load('test.html') |
|---|
| 156 | stream = tmpl.generate(title='Hello, world!') |
|---|
| [1076] | 157 | print(stream.render()) |
|---|
| [534] | 158 | |
|---|
| 159 | See the `API documentation <api/index.html>`_ for details on using Genshi via |
|---|
| 160 | the Python API. |
|---|
| 161 | |
|---|
| [1107] | 162 | .. _`template loader`: loader.html |
|---|
| [534] | 163 | |
|---|
| 164 | .. _`expressions`: |
|---|
| 165 | |
|---|
| 166 | ------------------------------------ |
|---|
| 167 | Template Expressions and Code Blocks |
|---|
| 168 | ------------------------------------ |
|---|
| 169 | |
|---|
| 170 | Python_ expressions can be used in text and directive arguments. An expression |
|---|
| 171 | is substituted with the result of its evaluation against the template data. |
|---|
| 172 | Expressions in text (which includes the values of non-directive attributes) need |
|---|
| 173 | to prefixed with a dollar sign (``$``) and usually enclosed in curly braces |
|---|
| 174 | (``{…}``). |
|---|
| 175 | |
|---|
| 176 | .. _python: http://www.python.org/ |
|---|
| 177 | |
|---|
| 178 | If the expression starts with a letter and contains only letters, digits, dots, |
|---|
| 179 | and underscores, the curly braces may be omitted. In all other cases, the |
|---|
| 180 | braces are required so that the template processor knows where the expression |
|---|
| [614] | 181 | ends: |
|---|
| [534] | 182 | |
|---|
| [614] | 183 | .. code-block:: pycon |
|---|
| 184 | |
|---|
| [534] | 185 | >>> from genshi.template import MarkupTemplate |
|---|
| 186 | >>> tmpl = MarkupTemplate('<em>${items[0].capitalize()} item</em>') |
|---|
| [1076] | 187 | >>> print(tmpl.generate(items=['first', 'second'])) |
|---|
| [534] | 188 | <em>First item</em> |
|---|
| 189 | |
|---|
| 190 | Expressions support the full power of Python. In addition, it is possible to |
|---|
| 191 | access items in a dictionary using “dotted notation” (i.e. as if they were |
|---|
| 192 | attributes), and vice-versa (i.e. access attributes as if they were items in a |
|---|
| [614] | 193 | dictionary): |
|---|
| [534] | 194 | |
|---|
| [614] | 195 | .. code-block:: pycon |
|---|
| 196 | |
|---|
| [534] | 197 | >>> from genshi.template import MarkupTemplate |
|---|
| 198 | >>> tmpl = MarkupTemplate('<em>${dict.foo}</em>') |
|---|
| [1076] | 199 | >>> print(tmpl.generate(dict={'foo': 'bar'})) |
|---|
| [534] | 200 | <em>bar</em> |
|---|
| 201 | |
|---|
| 202 | Because there are two ways to access either attributes or items, expressions |
|---|
| 203 | do not raise the standard ``AttributeError`` or ``IndexError`` exceptions, but |
|---|
| 204 | rather an exception of the type ``UndefinedError``. The same kind of error is |
|---|
| [576] | 205 | raised when you try to use a top-level variable that is not in the context data. |
|---|
| 206 | See `Error Handling`_ below for details on how such errors are handled. |
|---|
| [534] | 207 | |
|---|
| 208 | |
|---|
| [1013] | 209 | Escaping |
|---|
| 210 | ======== |
|---|
| 211 | |
|---|
| 212 | If you need to include a literal dollar sign in the output where Genshi would |
|---|
| 213 | normally detect an expression, you can simply add another dollar sign: |
|---|
| 214 | |
|---|
| 215 | .. code-block:: pycon |
|---|
| 216 | |
|---|
| 217 | >>> from genshi.template import MarkupTemplate |
|---|
| 218 | >>> tmpl = MarkupTemplate('<em>$foo</em>') # Wanted "$foo" as literal output |
|---|
| [1076] | 219 | >>> print(tmpl.generate()) |
|---|
| [1013] | 220 | Traceback (most recent call last): |
|---|
| 221 | ... |
|---|
| 222 | UndefinedError: "foo" not defined |
|---|
| 223 | >>> tmpl = MarkupTemplate('<em>$$foo</em>') |
|---|
| [1076] | 224 | >>> print(tmpl.generate()) |
|---|
| [1013] | 225 | <em>$foo</em> |
|---|
| 226 | |
|---|
| 227 | But note that this is not necessary if the characters following the dollar sign |
|---|
| 228 | do not qualify as an expression. For example, the following needs no escaping: |
|---|
| 229 | |
|---|
| 230 | .. code-block:: pycon |
|---|
| 231 | |
|---|
| 232 | >>> tmpl = MarkupTemplate('<script>$(function() {})</script>') |
|---|
| [1076] | 233 | >>> print(tmpl.generate()) |
|---|
| [1013] | 234 | <script>$(function() {})</script> |
|---|
| 235 | |
|---|
| 236 | On the other hand, Genshi will always replace two dollar signs in text with a |
|---|
| 237 | single dollar sign, so you'll need to use three dollar signs to get two in the |
|---|
| 238 | output: |
|---|
| 239 | |
|---|
| 240 | .. code-block:: pycon |
|---|
| 241 | |
|---|
| 242 | >>> tmpl = MarkupTemplate('<script>$$$("div")</script>') |
|---|
| [1076] | 243 | >>> print(tmpl.generate()) |
|---|
| [1013] | 244 | <script>$$("div")</script> |
|---|
| 245 | |
|---|
| 246 | |
|---|
| [534] | 247 | .. _`code blocks`: |
|---|
| 248 | |
|---|
| 249 | Code Blocks |
|---|
| 250 | =========== |
|---|
| 251 | |
|---|
| [725] | 252 | Templates also support full Python code blocks, using the ``<?python ?>`` |
|---|
| 253 | processing instruction in XML templates: |
|---|
| [534] | 254 | |
|---|
| [614] | 255 | .. code-block:: genshi |
|---|
| 256 | |
|---|
| [534] | 257 | <div> |
|---|
| 258 | <?python |
|---|
| 259 | from genshi.builder import tag |
|---|
| 260 | def greeting(name): |
|---|
| [765] | 261 | return tag.b('Hello, %s!' % name) ?> |
|---|
| [534] | 262 | ${greeting('world')} |
|---|
| 263 | </div> |
|---|
| 264 | |
|---|
| [614] | 265 | This will produce the following output: |
|---|
| [534] | 266 | |
|---|
| [725] | 267 | .. code-block:: xml |
|---|
| [614] | 268 | |
|---|
| [534] | 269 | <div> |
|---|
| 270 | <b>Hello, world!</b> |
|---|
| 271 | </div> |
|---|
| 272 | |
|---|
| [725] | 273 | In text templates (although only those using the new syntax introduced in |
|---|
| 274 | Genshi 0.5), code blocks use the special ``{% python %}`` directive: |
|---|
| 275 | |
|---|
| 276 | .. code-block:: genshitext |
|---|
| 277 | |
|---|
| 278 | {% python |
|---|
| 279 | from genshi.builder import tag |
|---|
| 280 | def greeting(name): |
|---|
| [765] | 281 | return 'Hello, %s!' % name |
|---|
| [725] | 282 | %} |
|---|
| 283 | ${greeting('world')} |
|---|
| 284 | |
|---|
| 285 | This will produce the following output:: |
|---|
| 286 | |
|---|
| 287 | Hello, world! |
|---|
| 288 | |
|---|
| 289 | |
|---|
| [534] | 290 | Code blocks can import modules, define classes and functions, and basically do |
|---|
| 291 | anything you can do in normal Python code. What code blocks can *not* do is to |
|---|
| [722] | 292 | produce content that is emitted directly tp the generated output. |
|---|
| [534] | 293 | |
|---|
| 294 | .. note:: Using the ``print`` statement will print to the standard output |
|---|
| 295 | stream, just as it does for other Python code in your application. |
|---|
| 296 | |
|---|
| [576] | 297 | Unlike expressions, Python code in ``<?python ?>`` processing instructions can |
|---|
| 298 | not use item and attribute access in an interchangeable manner. That means that |
|---|
| 299 | “dotted notation” is always attribute access, and vice-versa. |
|---|
| [534] | 300 | |
|---|
| [576] | 301 | The support for Python code blocks in templates is not supposed to encourage |
|---|
| 302 | mixing application code into templates, which is generally considered bad |
|---|
| 303 | design. If you're using many code blocks, that may be a sign that you should |
|---|
| 304 | move such code into separate Python modules. |
|---|
| 305 | |
|---|
| [654] | 306 | If you'd rather not allow the use of Python code blocks in templates, you can |
|---|
| 307 | simply set the ``allow_exec`` parameter (available on the ``Template`` and the |
|---|
| 308 | ``TemplateLoader`` initializers) to ``False``. In that case Genshi will raise |
|---|
| 309 | a syntax error when a ``<?python ?>`` processing instruction is encountered. |
|---|
| 310 | But please note that disallowing code blocks in templates does not turn Genshi |
|---|
| 311 | into a sandboxable template engine; there are sufficient ways to do harm even |
|---|
| 312 | using plain expressions. |
|---|
| 313 | |
|---|
| [534] | 314 | |
|---|
| 315 | .. _`error handling`: |
|---|
| 316 | |
|---|
| 317 | Error Handling |
|---|
| 318 | ============== |
|---|
| 319 | |
|---|
| [722] | 320 | By default, Genshi raises an ``UndefinedError`` if a template expression |
|---|
| 321 | attempts to access a variable that is not defined: |
|---|
| [534] | 322 | |
|---|
| [614] | 323 | .. code-block:: pycon |
|---|
| 324 | |
|---|
| [534] | 325 | >>> from genshi.template import MarkupTemplate |
|---|
| 326 | >>> tmpl = MarkupTemplate('<p>${doh}</p>') |
|---|
| [722] | 327 | >>> tmpl.generate().render('xhtml') |
|---|
| 328 | Traceback (most recent call last): |
|---|
| 329 | ... |
|---|
| 330 | UndefinedError: "doh" not defined |
|---|
| 331 | |
|---|
| 332 | You can change this behavior by setting the variable lookup mode to "lenient". |
|---|
| 333 | In that case, accessing undefined variables returns an `Undefined` object, |
|---|
| 334 | meaning that the expression does not fail immediately. See below for details. |
|---|
| 335 | |
|---|
| 336 | If you need to check whether a variable exists in the template context, use the |
|---|
| 337 | defined_ or the value_of_ function described below. To check for existence of |
|---|
| 338 | attributes on an object, or keys in a dictionary, use the ``hasattr()``, |
|---|
| 339 | ``getattr()`` or ``get()`` functions, or the ``in`` operator, just as you would |
|---|
| 340 | in regular Python code: |
|---|
| 341 | |
|---|
| 342 | >>> from genshi.template import MarkupTemplate |
|---|
| 343 | >>> tmpl = MarkupTemplate('<p>${defined("doh")}</p>') |
|---|
| [1076] | 344 | >>> print(tmpl.generate().render('xhtml')) |
|---|
| [722] | 345 | <p>False</p> |
|---|
| 346 | |
|---|
| 347 | .. note:: Lenient error handling was the default in Genshi prior to version 0.5. |
|---|
| 348 | Strict mode was introduced in version 0.4, and became the default in |
|---|
| 349 | 0.5. The reason for this change was that the lenient error handling |
|---|
| 350 | was masking actual errors in templates, thereby also making it harder |
|---|
| 351 | to debug some problems. |
|---|
| 352 | |
|---|
| 353 | |
|---|
| 354 | .. _`lenient`: |
|---|
| 355 | |
|---|
| 356 | Lenient Mode |
|---|
| 357 | ------------ |
|---|
| 358 | |
|---|
| 359 | If you instruct Genshi to use the lenient variable lookup mode, it allows you |
|---|
| 360 | to access variables that are not defined, without raising an ``UndefinedError``. |
|---|
| 361 | |
|---|
| 362 | This mode can be chosen by passing the ``lookup='lenient'`` keyword argument to |
|---|
| 363 | the template initializer, or by passing the ``variable_lookup='lenient'`` |
|---|
| 364 | keyword argument to the ``TemplateLoader`` initializer: |
|---|
| 365 | |
|---|
| 366 | .. code-block:: pycon |
|---|
| 367 | |
|---|
| 368 | >>> from genshi.template import MarkupTemplate |
|---|
| 369 | >>> tmpl = MarkupTemplate('<p>${doh}</p>', lookup='lenient') |
|---|
| [1076] | 370 | >>> print(tmpl.generate().render('xhtml')) |
|---|
| [534] | 371 | <p></p> |
|---|
| 372 | |
|---|
| 373 | You *will* however get an exception if you try to call an undefined variable, or |
|---|
| [614] | 374 | do anything else with it, such as accessing its attributes: |
|---|
| [534] | 375 | |
|---|
| [614] | 376 | .. code-block:: pycon |
|---|
| 377 | |
|---|
| [534] | 378 | >>> from genshi.template import MarkupTemplate |
|---|
| [722] | 379 | >>> tmpl = MarkupTemplate('<p>${doh.oops}</p>', lookup='lenient') |
|---|
| [1076] | 380 | >>> print(tmpl.generate().render('xhtml')) |
|---|
| [534] | 381 | Traceback (most recent call last): |
|---|
| 382 | ... |
|---|
| 383 | UndefinedError: "doh" not defined |
|---|
| 384 | |
|---|
| 385 | If you need to know whether a variable is defined, you can check its type |
|---|
| [614] | 386 | against the ``Undefined`` class, for example in a conditional directive: |
|---|
| [534] | 387 | |
|---|
| [614] | 388 | .. code-block:: pycon |
|---|
| 389 | |
|---|
| [534] | 390 | >>> from genshi.template import MarkupTemplate |
|---|
| [722] | 391 | >>> tmpl = MarkupTemplate('<p>${type(doh) is not Undefined}</p>', |
|---|
| 392 | ... lookup='lenient') |
|---|
| [1076] | 393 | >>> print(tmpl.generate().render('xhtml')) |
|---|
| [534] | 394 | <p>False</p> |
|---|
| 395 | |
|---|
| 396 | Alternatively, the built-in functions defined_ or value_of_ can be used in this |
|---|
| 397 | case. |
|---|
| 398 | |
|---|
| 399 | Custom Modes |
|---|
| 400 | ------------ |
|---|
| 401 | |
|---|
| 402 | In addition to the built-in "lenient" and "strict" modes, it is also possible to |
|---|
| 403 | use a custom error handling mode. For example, you could use lenient error |
|---|
| 404 | handling in a production environment, while also logging a warning when an |
|---|
| 405 | undefined variable is referenced. |
|---|
| 406 | |
|---|
| 407 | See the API documentation of the ``genshi.template.eval`` module for details. |
|---|
| 408 | |
|---|
| 409 | |
|---|
| 410 | Built-in Functions & Types |
|---|
| 411 | ========================== |
|---|
| 412 | |
|---|
| 413 | The following functions and types are available by default in template code, in |
|---|
| 414 | addition to the standard built-ins that are available to all Python code. |
|---|
| 415 | |
|---|
| 416 | .. _`defined`: |
|---|
| 417 | |
|---|
| 418 | ``defined(name)`` |
|---|
| 419 | ----------------- |
|---|
| 420 | This function determines whether a variable of the specified name exists in |
|---|
| 421 | the context data, and returns ``True`` if it does. |
|---|
| 422 | |
|---|
| 423 | .. _`value_of`: |
|---|
| 424 | |
|---|
| 425 | ``value_of(name, default=None)`` |
|---|
| 426 | -------------------------------- |
|---|
| 427 | This function returns the value of the variable with the specified name if |
|---|
| 428 | such a variable is defined, and returns the value of the ``default`` |
|---|
| 429 | parameter if no such variable is defined. |
|---|
| 430 | |
|---|
| 431 | .. _`Markup`: |
|---|
| 432 | |
|---|
| 433 | ``Markup(text)`` |
|---|
| 434 | ---------------- |
|---|
| 435 | The ``Markup`` type marks a given string as being safe for inclusion in markup, |
|---|
| 436 | meaning it will *not* be escaped in the serialization stage. Use this with care, |
|---|
| 437 | as not escaping a user-provided string may allow malicious users to open your |
|---|
| 438 | web site to cross-site scripting attacks. |
|---|
| 439 | |
|---|
| 440 | .. _`Undefined`: |
|---|
| 441 | |
|---|
| 442 | ``Undefined`` |
|---|
| 443 | ---------------- |
|---|
| 444 | The ``Undefined`` type can be used to check whether a reference variable is |
|---|
| 445 | defined, as explained in `error handling`_. |
|---|
| 446 | |
|---|
| 447 | |
|---|
| 448 | .. _`directives`: |
|---|
| 449 | |
|---|
| 450 | ------------------- |
|---|
| 451 | Template Directives |
|---|
| 452 | ------------------- |
|---|
| 453 | |
|---|
| 454 | Directives provide control flow functionality for templates, such as conditions |
|---|
| 455 | or iteration. As the syntax for directives depends on whether you're using |
|---|
| 456 | markup or text templates, refer to the |
|---|
| 457 | `XML Template Language <xml-templates.html>`_ or |
|---|
| 458 | `Text Template Language <text-templates.html>`_ pages for information. |
|---|