Edgewall Software

Changes between Version 32 and Version 33 of MarkupTemplates


Ignore:
Timestamp:
Sep 8, 2006, 10:12:33 AM (18 years ago)
Author:
cmlenz
Comment:

Move to reStructuredText

Legend:

Unmodified
Added
Removed
Modified
  • MarkupTemplates

    v32 v33  
    1 = Markup Templates =
    2 
    3 The most important feature provided by the Markup package is a template engine.
    4 
    5 Templates are XML files of some kind (such as XHTML) that include ''[MarkupTemplates#Directives processing directives]'' (elements or attributes identified by a separate namespace) that affect how the template is rendered, and ''[MarkupTemplates#Expressions template expressions]'' that are dynamically substituted by variable data.
    6 
    7 This documentation is a work in progress and as of yet rather incomplete.
    8 
    9   ''Note that Markup templates are ''very'' similar to [http://kid-templating.org/ Kid] templates, so you may want to read the awesome [http://kid-templating.org/language.html language specification] for Kid for a more comprehensive guide. There exist some substantial differences between Kid and Markup, but the set of directives is basically the same (except for `py:extends` and `py:layout`, which don't exist in Markup, and `py:match`, which uses XPath in Markup).''
    10 
    11 [[PageOutline(2-3, Table of Contents, inline)]]
    12 
    13 == Python API ==
    14 
    15 The Python code required for templating with Markup is generally based on the following pattern:
    16 
    17  * Attain a `Template` object from a string or file object containing the template XML source. This can either be done directly, or through a `TemplateLoader` instance.
    18  * Call the `generate()` method of the template, passing any data that should be made available to the template as keyword arguments.
    19  * Serialize the resulting [MarkupStream stream] using its `render()` method.
    20 
    21 For example:
    22 
    231{{{
    24 #!python
    25 from markup.template import Template
    26 
    27 tmpl = Template('<h1>$title</h1>')
    28 stream = tmpl.generate(title='Hello, world!')
    29 print stream.render('xhtml')
    30 }}}
    31 
    32 That code would produce the following output:
    33 
    34 {{{
    35 #!xml
    36 <h1>Hello, world!</h1>
    37 }}}
    38 
    39 However, if you want [MarkupTemplates#Includes includes] to work, you should attain the template instance through a `TemplateLoader`, and load the template from a file:
    40 
    41 {{{
    42 #!python
    43 from markup.template import TemplateLoader
    44 
    45 loader = TemplateLoader([templates_dir])
    46 tmpl = loader.load('test.html')
    47 stream = tmpl.generate(title='Hello, world!')
    48 print stream.render('xhtml')
    49 }}}
    50 
    51 See the [ApiDocs API docs] of the [ApiDocs/MarkupTemplate markup.template] and [ApiDocs/MarkupCore markup.core] modules for more details.
    52 
    53 == Expressions ==
    54 
    55 Python expressions can be used in text and attribute values. An expression is substituted with the result of its evaluation against the template data. Expressions need to prefixed with a dollar sign (`$`) and usually enclosed in curly braces (`{…}`).
    56 
    57 If the expression starts with a letter and contains only letters and digits, the curly braces may be omitted. In all other cases, the braces are required so that the template processors knows where the expression ends:
    58 
    59 {{{
    60 >>> from markup.template import Context, Template
    61 >>> tmpl = Template('<em>${items[0].capitalize()} item</em>')
    62 >>> print tmpl.generate(Context(items=['first', 'second']))
    63 <em>First item</em>
    64 }}}
    65 
    66 Expressions support the full power of Python. In addition, it is possible to:
    67  * access items in a dictionary using ''“dotted notation”'' (i.e. as if they were attributes)
    68  * access attributes using item access (i.e. as if they were items in a dictionary)
    69 
    70 {{{
    71 >>> from markup.template import Context, Template
    72 >>> tmpl = Template('<em>${dict.foo}</em>')
    73 >>> print tmpl.generate(Context(dict={'foo': 'bar'}))
    74 <em>bar</em>
    75 }}}
    76 
    77 == Directives ==
    78 
    79 ''Directives'' are elements and/or attributes in the template that are identified by the namespace `http://markup.edgewall.org/`. They can affect how the template is rendered in a number of ways: Markup provides directives for conditionals and looping, among others.
    80 
    81 To use directives in a template, the namespace should be declared, which is usually done on the root element:
    82 
    83 {{{
    84 #!xml
    85 <html xmlns="http://www.w3.org/1999/xhtml"
    86       xmlns:py="http://markup.edgewall.org/"
    87       lang="en">
    88   ...
    89 </html>
    90 }}}
    91 
    92 In this example, the default namespace is set to the XHTML namespace, and the namespace for Markup directives is bound to the prefix “py”.
    93 
    94 All directives can be applied as attributes, and some can also be used as elements. The `if` directives for conditionals, for example, can be used in both ways:
    95 
    96 {{{
    97 #!xml
    98 <html xmlns="http://www.w3.org/1999/xhtml"
    99       xmlns:py="http://markup.edgewall.org/"
    100       lang="en">
    101   ...
    102   <div py:if="foo">
    103     <p>Bar</p>
    104   </div>
    105   ...
    106 </html>
    107 }}}
    108 
    109 This is basically equivalent to the following:
    110 
    111 {{{
    112 #!xml
    113 <html xmlns="http://www.w3.org/1999/xhtml"
    114       xmlns:py="http://markup.edgewall.org/"
    115       lang="en">
    116   ...
    117   <py:if test="foo">
    118     <div>
     2#!rst
     3============================
     4Markup XML Template Language
     5============================
     6
     7Markup provides a simple XML-based template language that is heavily inspired
     8by Kid_, which in turn was inspired by a number of existing template languages,
     9namely XSLT_, TAL_, and PHP_.
     10
     11.. _kid: http://kid-templating.org/
     12.. _python: http://www.python.org/
     13.. _xslt: http://www.w3.org/TR/xslt
     14.. _tal: http://www.zope.org/Wikis/DevSite/Projects/ZPT/TAL
     15.. _php: http://www.php.net/
     16
     17This document describes the template language and will be most useful as
     18reference to those developing Markup templates. Templates are XML files of some
     19kind (such as XHTML) that include processing directives_ (elements or
     20attributes identified by a separate namespace) that affect how the template is
     21rendered, and template expressions_ that are dynamically substituted by
     22variable data.
     23
     24
     25.. contents:: Contents
     26   :depth: 3
     27.. sectnum::
     28
     29
     30.. _`expressions`:
     31
     32--------------------
     33Template Expressions
     34--------------------
     35
     36Python_ expressions can be used in text and attribute values. An expression is
     37substituted with the result of its evaluation against the template data.
     38Expressions need to prefixed with a dollar sign (``$``) and usually enclosed in
     39curly braces (``{…}``).
     40
     41If the expression starts with a letter and contains only letters and digits,
     42the curly braces may be omitted. In all other cases, the braces are required so
     43that the template processors knows where the expression ends::
     44
     45  >>> from markup.template import Context, Template
     46  >>> tmpl = Template('<em>${items[0].capitalize()} item</em>')
     47  >>> print tmpl.generate(Context(items=['first', 'second']))
     48  <em>First item</em>
     49
     50Expressions support the full power of Python. In addition, it is possible to
     51access items in a dictionary using “dotted notation” (i.e. as if they were
     52attributes), and vice-versa (i.e. access attributes as if they were items in a
     53dictionary)::
     54
     55  >>> from markup.template import Context, Template
     56  >>> tmpl = Template('<em>${dict.foo}</em>')
     57  >>> print tmpl.generate(Context(dict={'foo': 'bar'}))
     58  <em>bar</em>
     59
     60
     61.. _`directives`:
     62
     63-------------------
     64Template Directives
     65-------------------
     66
     67Directives are elements and/or attributes in the template that are identified
     68by the namespace ``http://markup.edgewall.org/``. They can affect how the
     69template is rendered in a number of ways: Markup provides directives for
     70conditionals and looping, among others.
     71
     72To use directives in a template, the namespace should be declared, which is
     73usually done on the root element::
     74
     75  <html xmlns="http://www.w3.org/1999/xhtml"
     76        xmlns:py="http://markup.edgewall.org/"
     77        lang="en">
     78    ...
     79  </html>
     80
     81In this example, the default namespace is set to the XHTML namespace, and the
     82namespace for Markup directives is bound to the prefix “py”.
     83
     84All directives can be applied as attributes, and some can also be used as
     85elements. The ``if`` directives for conditionals, for example, can be used in
     86both ways::
     87
     88  <html xmlns="http://www.w3.org/1999/xhtml"
     89        xmlns:py="http://markup.edgewall.org/"
     90        lang="en">
     91    ...
     92    <div py:if="foo">
    11993      <p>Bar</p>
    12094    </div>
    121   </py:if>
    122   ...
    123 </html>
    124 }}}
    125 
    126 The rationale behind the second form is that directives do not always map naturally to elements in the template. In such cases, the [MarkupTemplates#py:strip py:strip] directive can be used to strip off the unwanted element, or the directive can simply be used as an element.
    127 
    128 === `py:content` ===
    129 
    130 This directive replaces any nested content with the result of evaluating the expression.
    131 
    132 {{{
    133 #!xml
    134 <ul>
    135   <li py:content="bar">Hello</li>
    136 </ul>
    137 }}}
    138 
    139 Given `bar='Bye'` in the context data, this would produce:
    140 
    141 {{{
    142 #!xml
    143 <ul>
    144   <li>Bye</li>
    145 </ul>
    146 }}}
     95    ...
     96  </html>
     97
     98This is basically equivalent to the following::
     99
     100  <html xmlns="http://www.w3.org/1999/xhtml"
     101        xmlns:py="http://markup.edgewall.org/"
     102        lang="en">
     103    ...
     104    <py:if test="foo">
     105      <div>
     106        <p>Bar</p>
     107      </div>
     108    </py:if>
     109    ...
     110  </html>
     111
     112The rationale behind the second form is that directives do not always map
     113naturally to elements in the template. In such cases, the ``py:strip``
     114directive can be used to strip off the unwanted element, or the directive can
     115simply be used as an element.
     116
     117
     118Available Directives
     119====================
     120
     121
     122.. _`py:attrs`:
     123
     124``py:attrs``
     125------------
     126
     127This directive adds, modifies or removes attributes from the element::
     128
     129  <ul>
     130    <li py:attrs="foo">Bar</li>
     131  </ul>
     132
     133Given ``foo={'class': 'collapse'}`` in the template context, this would
     134produce::
     135
     136  <ul>
     137    <li class="collapse">Bar</li>
     138  </ul>
     139
     140Attributes with the value ``None`` are omitted, so given ``foo={'class': None}``
     141in the context for the same template this would produce::
     142
     143  <ul>
     144    <li>Bar</li>
     145  </ul>
    147146
    148147This directive can only be used as an attribute.
    149148
    150 === `py:replace` ===
    151 
    152 This directive replaces the element itself with the result of evaluating the expression.
    153 
    154 {{{
    155 #!xml
    156 <div>
    157   <span py:replace="bar">Hello</span>
    158 </div>
    159 }}}
    160 
    161 Given `bar='Bye'` in the context data, this would produce:
    162 
    163 {{{
    164 #!xml
    165 <div>
    166   Bye
    167 </div>
    168 }}}
     149
     150.. _`py:choose`:
     151.. _`py:when`:
     152.. _`py:otherwise`:
     153
     154``py:choose`` / ``py:when`` / ``py:otherwise``
     155----------------------------------------------
     156
     157This set of directives provides advanced contional processing for rendering one
     158of several alternatives. The first matching ``py:when`` branch is rendered, or,
     159if no ``py:when`` branch matches, the ``py:otherwise`` branch is be rendered.
     160
     161If the ``py:choose`` directive is empty the nested ``py:when`` directives will
     162be tested for truth::
     163
     164  <div py:choose="">
     165    <span py:when="0 == 1">0</span>
     166    <span py:when="1 == 1">1</span>
     167    <span py:otherwise="">2</span>
     168  </div>
     169
     170This would produce the following output::
     171
     172  <div>
     173    <span>1</span>
     174  </div>
     175
     176If the ``py:choose`` directive contains an expression the nested ``py:when``
     177directives will be tested for equality to the parent ``py:choose`` value::
     178
     179  <div py:choose="1">
     180    <span py:when="0">0</span>
     181    <span py:when="1">1</span>
     182    <span py:otherwise="">2</span>
     183  </div>
     184
     185This would produce the following output::
     186
     187  <div>
     188    <span>1</span>
     189  </div>
     190
     191
     192.. _`py:content`:
     193
     194``py:content``
     195--------------
     196
     197This directive replaces any nested content with the result of evaluating the
     198expression::
     199
     200  <ul>
     201    <li py:content="bar">Hello</li>
     202  </ul>
     203
     204Given ``bar='Bye'`` in the context data, this would produce::
     205
     206  <ul>
     207    <li>Bye</li>
     208  </ul>
    169209
    170210This directive can only be used as an attribute.
    171211
    172 === `py:attrs` ===
    173 
    174 This directive adds, modifies or removes attributes from the element.
    175 
    176 {{{
    177 #!xml
    178 <ul>
    179   <li py:attrs="foo">Bar</li>
    180 </ul>
    181 }}}
    182 
    183 Given `foo={'class': 'collapse'}` in the template context, this would produce:
    184 
    185 {{{
    186 #!xml
    187 <ul>
    188   <li class="collapse">Bar</li>
    189 </ul>
    190 }}}
    191 
    192 Attributes with the value `None` are omitted, so given `foo={'class': None}` in the context for the same template this would produce:
    193 
    194 {{{
    195 #!xml
    196 <ul>
    197   <li>Bar</li>
    198 </ul>
    199 }}}
     212
     213.. _`py:def`:
     214.. _`macros`:
     215
     216``py:def``
     217----------
     218
     219The ``py:def`` directive can be used to create macros, i.e. snippets of
     220template code that have a name and optionally some parameters, and that can be
     221inserted in other places::
     222
     223  <div>
     224    <p py:def="greeting(name)" class="greeting">
     225      Hello, ${name}!
     226    </p>
     227    ${greeting('world')}
     228    ${greeting('everyone else')}
     229  </div>
     230
     231The above would be rendered to::
     232
     233  <div>
     234    <p class="greeting">
     235      Hello, world!
     236    </p>
     237    <p class="greeting">
     238      Hello, everyone else!
     239    </p>
     240  </div>
     241
     242If a macro doesn't require parameters, it can be defined as well as called
     243without the parenthesis. For example::
     244
     245  <div>
     246    <p py:def="greeting" class="greeting">
     247      Hello, world!
     248    </p>
     249    ${greeting}
     250  </div>
     251
     252The above would be rendered to::
     253
     254  <div>
     255    <p class="greeting">
     256      Hello, world!
     257    </p>
     258  </div>
     259
     260This directive can also be used as an element::
     261
     262  <div>
     263    <py:def function="greeting(name)">
     264      <p class="greeting">Hello, ${name}!</p>
     265    </py:def>
     266  </div>
     267
     268
     269.. _`py:for`:
     270
     271``py:for``
     272----------
     273
     274The element is repeated for every item in an iterable::
     275
     276  <ul>
     277    <li py:for="item in items">${item}</li>
     278  </ul>
     279
     280Given ``items=[1, 2, 3]`` in the context data, this would produce::
     281
     282  <ul>
     283    <li>1</li><li>2</li><li>3</li>
     284  </ul>
     285
     286This directive can also be used as an element::
     287
     288  <ul>
     289    <py:for each="item in items">
     290      <li>${item}</li>
     291    </py:for>
     292  </ul>
     293
     294
     295.. _`py:if`:
     296
     297``py:if``
     298------------
     299
     300The element is only rendered if the expression evaluates to a truth value::
     301
     302  <div>
     303    <b py:if="foo">${bar}</b>
     304  </div>
     305
     306Given the data ``foo=True`` and ``bar='Hello'`` in the template context, this
     307would produce::
     308
     309  <div>
     310    <b>Hello</b>
     311  </div>
     312
     313This directive can also be used as an element::
     314
     315  <div>
     316    <py:if test="foo">
     317      <b>${bar}</b>
     318    </py:if>
     319  </div>
     320
     321
     322.. _`py:match`:
     323.. _Match Templates:
     324
     325``py:match``
     326------------
     327
     328This directive defines a *match template*: given an XPath expression, it
     329replaces any element in the template that matches the expression with its own
     330content.
     331
     332For example, the match template defined in the following template matches any
     333element with the tag name “greeting”::
     334
     335  <div>
     336    <span py:match="greeting">
     337      Hello ${select('@name')}
     338    </span>
     339    <greeting name="Dude" />
     340  </div>
     341
     342This would result in the following output::
     343
     344  <div>
     345    <span>
     346      Hello Dude
     347    </span>
     348  </div>
     349
     350Inside the body of a ``py:match`` directive, the ``select(path)`` function is
     351made available so that parts or all of the original element can be incorporated
     352in the output of the match template. See [wiki:MarkupStream#UsingXPath] for
     353more information about this function.
     354
     355This directive can also be used as an element::
     356
     357  <div>
     358    <py:match path="greeting">
     359      <span>Hello ${select('@name')}</span>
     360    </py:match>
     361    <greeting name="Dude" />
     362  </div>
     363
     364
     365.. _`py:replace`:
     366
     367``py:replace``
     368--------------
     369
     370This directive replaces the element itself with the result of evaluating the
     371expression::
     372
     373  <div>
     374    <span py:replace="bar">Hello</span>
     375  </div>
     376
     377Given ``bar='Bye'`` in the context data, this would produce::
     378
     379  <div>
     380    Bye
     381  </div>
    200382
    201383This directive can only be used as an attribute.
    202384
    203 === `py:strip` ===
    204 
    205 This directive conditionally strips the top-level element from the output. When the value of the `py:strip` attribute evaluates to `True`, the element
    206 is stripped from the output:
    207 
    208 {{{
    209 #!xml
    210 <div>
    211   <div py:strip="True"><b>foo</b></div>
    212 </div>
    213 }}}
    214 
    215 This would be rendered as:
    216 
    217 {{{
    218 #!xml
    219 <div>
    220   <b>foo</b>
    221 </div>
    222 }}}
    223 
    224 As a shorthand, if the value of the `py:strip` attribute is empty, that has the same effect as using a truth value (i.e. the element is stripped).
    225 
    226 === `py:if` ===
    227 
    228 The element is only rendered if the expression evaluates to a truth value.
    229 
    230 {{{
    231 #!xml
    232 <div>
    233   <b py:if="foo">${bar}</b>
    234 </div>
    235 }}}
    236 
    237 Given the data `foo=True` and `bar='Hello'` in the template context, this would produce:
    238 
    239 {{{
    240 #!xml
    241 <div>
    242   <b>Hello</b>
    243 </div>
    244 }}}
    245 
    246 This directive can also be used as an element:
    247 
    248 {{{
    249 #!xml
    250 <div>
    251   <py:if test="foo">
    252     <b>${bar}</b>
    253   </py:if>
    254 </div>
    255 }}}
    256 
    257 
    258 === `py:choose` / `py:when` / `py:otherwise` ===
    259 
    260 This set of directives provides advanced contional processing for rendering one of several alternatives.  The first matching `py:when` directive will be rendered or, if no `py:when` directive matches, the `py:otherwise` directive will be rendered.
    261 
    262 If the `py:choose` directive is empty the nested `py:when` directives will be tested for truth:
    263 
    264 {{{
    265 #!xml
    266 <div py:choose="">
    267   <span py:when="0 == 1">0</span>
    268   <span py:when="1 == 1">1</span>
    269   <span py:otherwise="">2</span>
    270 </div>
    271 }}}
    272 
    273 This would produce the following output:
    274 
    275 {{{
    276 #!xml
    277 <div>
    278   <span>1</span>
    279 </div>
    280 }}}
    281 
    282 If the `py:choose` directive contains an expression the nested `py:when` directives will be tested for equality to the parent `py:choose` value:
    283 
    284 {{{
    285 #!xml
    286 <div py:choose="1">
    287   <span py:when="0">0</span>
    288   <span py:when="1">1</span>
    289   <span py:otherwise="">2</span>
    290 </div>
    291 }}}
    292 
    293 This would produce the following output:
    294 
    295 {{{
    296 #!xml
    297 <div>
    298   <span>1</span>
    299 </div>
    300 }}}
    301 
    302 === `py:for` ===
    303 
    304 The element is repeated for every item in an iterable:
    305 
    306 {{{
    307 #!xml
    308 <ul>
    309   <li py:for="item in items">${item}</li>
    310 </ul>
    311 }}}
    312 
    313 Given `items=[1, 2, 3]` in the context data, this would produce:
    314 
    315 {{{
    316 #!xml
    317 <ul>
    318   <li>1</li><li>2</li><li>3</li>
    319 </ul>
    320 }}}
    321 
    322 This directive can also be used as an element:
    323 
    324 {{{
    325 #!xml
    326 <ul>
    327   <py:for each="item in items">
    328     <li>${item}</li>
    329   </py:for>
    330 </ul>
    331 }}}
    332 
    333 === `py:def` ===
    334 
    335 The `py:def` directive can be used to create ''template functions'', i.e. snippets of template code that have a name and optionally some parameters, and that can be inserted in other places.
    336 
    337 {{{
    338 #!xml
    339 <div>
    340   <p py:def="greeting(name)" class="greeting">
    341     Hello, ${name}!
    342   </p>
    343   ${greeting('world')}
    344   ${greeting('everyone else')}
    345 </div>
    346 }}}
    347 
    348 The above would be rendered to:
    349 
    350 {{{
    351 #!xml
    352 <div>
    353   <p class="greeting">
    354     Hello, world!
    355   </p>
    356   <p class="greeting">
    357     Hello, everyone else!
    358   </p>
    359 </div>
    360 }}}
    361 
    362 If a template function doesn't require parameters, it can be defined as well as called without the parenthesis. For example:
    363 
    364 {{{
    365 #!xml
    366 <div>
    367   <p py:def="greeting" class="greeting">
    368     Hello, world!
    369   </p>
    370   ${greeting}
    371 </div>
    372 }}}
    373 
    374 The above would be rendered to:
    375 
    376 {{{
    377 #!xml
    378 <div>
    379   <p class="greeting">
    380     Hello, world!
    381   </p>
    382 </div>
    383 }}}
    384 
    385 This directive can also be used as an element:
    386 
    387 {{{
    388 #!xml
    389 <div>
    390   <py:def function="greeting(name)">
    391     <p class="greeting">Hello, ${name}!</p>
    392   </py:def>
    393 </div>
    394 }}}
    395 
    396 === `py:with` ===
    397 
    398 The `py:with` directive lets you assign expressions to variables, which can be used to make expressions inside the directive less verbose and more efficient. For example, if you need use the expression `author.posts` more than once, and that actually results in a database query, assigning the results to a variable using this directive would probably help.
    399 
    400 For example:
    401 
    402 {{{
    403 #!xml
    404 <div>
    405   <span py:with="y=7; z=x+10">$x $y $z</span>
    406 </div>
    407 }}}
    408 
    409 Given `x=42` in the context data, this would produce:
    410 
    411 {{{
    412 #!xml
    413 <div>
    414   <span>42 7 52</span>
    415 </div>
    416 }}}
    417 
    418 This directive can also be used as an element:
    419 
    420 {{{
    421 #!xml
    422 <div>
    423   <py:with vars="y=7; z=x+10">$x $y $z</py:with>
    424 </div>
    425 }}}
    426 
    427 Note that if a variable of the same name already existed outside of the scope of the `py:with` directive, it will '''not''' be overwritten. Instead, it will have the same value it had prior to the `py:with` assignment. Effectively, this means that variables are immutable in Markup.
    428 
    429 === `py:match` ===
    430 
    431 This directive defines a ''match template'': given an XPath expression, it replaces any element in the template that matches the expression with its own content.
    432 
    433 For example, the match template defined in the following template matches any element with the tag name “greeting”:
    434 
    435 {{{
    436 #!xml
    437 <div>
    438   <span py:match="greeting">
    439     Hello ${select('@name')}
    440   </span>
    441   <greeting name="Dude" />
    442 </div>
    443 }}}
    444 
    445 This would result in the following output:
    446 
    447 {{{
    448 #!xml
    449 <div>
    450   <span>
    451     Hello Dude
    452   </span>
    453 </div>
    454 }}}
    455 
    456 Inside the body of a `py:match` directive, the `select(path)` function is made available so that parts or all of the original element can be incorporated in the output of the match template. See [wiki:MarkupStream#UsingXPath] for more information about this function.
    457 
    458 This directive can also be used as an element:
    459 
    460 {{{
    461 #!xml
    462 <div>
    463   <py:match path="greeting">
    464     <span>Hello ${select('@name')}</span>
    465   </py:match>
    466   <greeting name="Dude" />
    467 </div>
    468 }}}
    469 
    470 == Processing order ==
    471 
    472 Directives are evaluated in the following order:
    473 
    474   * [MarkupTemplates#py:def py:def]
    475   * [MarkupTemplates#py:match py:match]
    476   * [MarkupTemplates#py:when py:when]
    477   * [MarkupTemplates#py:otherwise py:otherwise]
    478   * [MarkupTemplates#py:for py:for]
    479   * [MarkupTemplates#py:if py:if]
    480   * [MarkupTemplates#py:choose py:choose]
    481   * [MarkupTemplates#py:with py:with]
    482   * [MarkupTemplates#py:replace py:replace]
    483   * [MarkupTemplates#py:content py:content]
    484   * [MarkupTemplates#py:attrs py:attrs]
    485   * [MarkupTemplates#py:strip py:strip]
    486 
    487 == Includes ==
    488 
    489 To reuse common snippets of template code, you can include other files using [http://www.w3.org/TR/xinclude/ XInclude].
    490 
    491 For this, you need to declare the XInclude namespace (commonly bound to the prefix "xi") and use the `<xi:include>` element where you want the external file to be pulled in:
    492 
    493 {{{
    494 #!xml
    495 <html xmlns="http://www.w3.org/1999/xhtml"
    496       xmlns:py="http://markup.edgewall.org/"
    497       xmlns:xi="http://www.w3.org/2001/XInclude">
    498   <xi:include href="base.html" />
    499   ...
    500 </html>
    501 }}}
    502 
    503 Include paths are relative to the filename of the template currently being processed. So if the example above was in the file "`myapp/index.html`" (relative to the template search path), the XInclude processor would look for the included file at "`myapp/base.html`". You can also use Unix-style relative paths, for example "`../base.html`" to look in the parent directory.
    504 
    505 Any content included this way is inserted into the generated output instead of the `<xi:include>` element. The included template sees the same context data. [wiki:MarkupTemplates#py:match Match templates] and [wiki:MarkupTemplates#py:def template functions] in the included template are also available to the including template after the point it was included.
    506 
    507 By default, an error will be raised if an included file is not found. If that's not what you want, you can specify fallback content that should be used if the include fails. For example, to to make the include above fail silently, you'd write:
    508 
    509 {{{
    510 #!xml
     385
     386.. _`py:strip`:
     387
     388``py:strip``
     389------------
     390
     391This directive conditionally strips the top-level element from the output. When
     392the value of the ``py:strip`` attribute evaluates to ``True``, the element is
     393stripped from the output::
     394
     395  <div>
     396    <div py:strip="True"><b>foo</b></div>
     397  </div>
     398
     399This would be rendered as::
     400
     401  <div>
     402    <b>foo</b>
     403  </div>
     404
     405As a shorthand, if the value of the ``py:strip`` attribute is empty, that has
     406the same effect as using a truth value (i.e. the element is stripped).
     407
     408
     409.. _`with`:
     410
     411``py:with``
     412-----------
     413
     414The ``py:with`` directive lets you assign expressions to variables, which can
     415be used to make expressions inside the directive less verbose and more
     416efficient. For example, if you need use the expression ``author.posts`` more
     417than once, and that actually results in a database query, assigning the results
     418to a variable using this directive would probably help.
     419
     420For example::
     421
     422  <div>
     423    <span py:with="y=7; z=x+10">$x $y $z</span>
     424  </div>
     425
     426Given ``x=42`` in the context data, this would produce::
     427
     428  <div>
     429    <span>42 7 52</span>
     430  </div>
     431
     432This directive can also be used as an element::
     433
     434  <div>
     435    <py:with vars="y=7; z=x+10">$x $y $z</py:with>
     436  </div>
     437
     438Note that if a variable of the same name already existed outside of the scope
     439of the ``py:with`` directive, it will **not** be overwritten. Instead, it
     440will have the same value it had prior to the ``py:with`` assignment.
     441Effectively, this means that variables are immutable in Markup.
     442
     443
     444.. _order:
     445
     446Processing Order
     447================
     448
     449It is possible to attach multiple directives to a single element, although not
     450all combinations make sense. When multiple directives are encountered, they are
     451processed in the following order:
     452
     453#. `py:def`_
     454#. `py:match`_
     455#. `py:when`_
     456#. `py:otherwise`_
     457#. `py:for`_
     458#. `py:if`_
     459#. `py:choose`_
     460#. `py:with`_
     461#. `py:replace`_
     462#. `py:content`_
     463#. `py:attrs`_
     464#. `py:strip`_
     465
     466
     467.. _includes:
     468
     469--------
     470Includes
     471--------
     472
     473To reuse common snippets of template code, you can include other files using
     474XInclude_.
     475
     476.. _xinclude: http://www.w3.org/TR/xinclude/
     477
     478For this, you need to declare the XInclude namespace (commonly bound to the
     479prefix “xi”) and use the ``<xi:include>`` element where you want the external
     480file to be pulled in::
     481
     482  <html xmlns="http://www.w3.org/1999/xhtml"
     483        xmlns:py="http://markup.edgewall.org/"
     484        xmlns:xi="http://www.w3.org/2001/XInclude">
     485    <xi:include href="base.html" />
     486    ...
     487  </html>
     488
     489Include paths are relative to the filename of the template currently being
     490processed. So if the example above was in the file "``myapp/index.html``"
     491(relative to the template search path), the XInclude processor would look for
     492the included file at "``myapp/base.html``". You can also use Unix-style
     493relative paths, for example "``../base.html``" to look in the parent directory.
     494
     495Any content included this way is inserted into the generated output instead of
     496the ``<xi:include>`` element. The included template sees the same context data.
     497`Match templates`_ and `macros`_ in the included template are also available to
     498the including template after the point it was included.
     499
     500By default, an error will be raised if an included file is not found. If that's
     501not what you want, you can specify fallback content that should be used if the
     502include fails. For example, to to make the include above fail silently, you'd
     503write:
     504
    511505  <xi:include href="base.html"><xi:fallback /></xi:include>
    512 }}}
    513 
    514 See the [http://www.w3.org/TR/xinclude/ XInclude specification] for more about fallback content. Note though that Markup currently only supports a small subset of XInclude.
    515 
    516 Incudes in Markup are fully dynamic: Just like normal attributes, the `href` attribute accepts [MarkupTemplates#TemplateExpressions expressions], and [MarkupTemplates#TemplateDirectives template directives] can be used on the `<xi:include />` element just as on any other element, meaning you can do things like conditional includes:
    517 
    518 {{{
    519 #!xml
     506
     507See the XInclude_ for more about fallback content. Note though that Markup
     508currently only supports a small subset of XInclude.
     509
     510Incudes in Markup are fully dynamic: Just like normal attributes, the `href`
     511attribute accepts expressions_, and directives_ can be used on the
     512``<xi:include />`` element just as on any other element, meaning you can do
     513things like conditional includes::
     514
    520515  <xi:include href="${name}.html" py:if="not in_popup"
    521516              py:for="name in ('foo', 'bar', 'baz')" />
     517
     518
     519.. _comments:
     520
     521--------
     522Comments
     523--------
     524
     525Normal XML/HTML comment syntax can be used in templates::
     526
     527  <!-- this is a comment -->
     528
     529However, such comments get passed through the processing pipeline and are by
     530default included in the final output. If that's not desired, prefix the comment
     531text with an exclamation mark::
     532
     533  <!-- !this is a comment too, but one that will be stripped from the output -->
     534
     535Note that it does not matter whether there's whitespace before or after the
     536exclamation mark, so the above could also be written as follows::
     537
     538  <!--! this is a comment too, but one that will be stripped from the output -->
    522539}}}
    523 
    524 == Comments ==
    525 
    526 Normal XML/HTML comment syntax can be used in templates:
    527 
    528 {{{
    529 #!xml
    530 <!-- this is a comment -->
    531 }}}
    532 
    533 However, such comments get passed through the processing pipeline and are by default included in the final output. If that's not desired, prefix the comment text with an exclamation mark:
    534 
    535 {{{
    536 #!xml
    537 <!-- !this is a comment too, but one that will be stripped from the output -->
    538 }}}
    539 
    540 Note that it does not matter whether there's whitespace before or after the exclamation mark, so the above could also be written as follows:
    541 
    542 {{{
    543 #!xml
    544 <!--! this is a comment too, but one that will be stripped from the output -->
    545 }}}
    546 
    547540----
    548541See also: MarkupGuide, MarkupRecipes