Edgewall Software

Changes between Version 35 and Version 36 of MarkupTemplates


Ignore:
Timestamp:
Sep 11, 2006, 6:05:25 PM (18 years ago)
Author:
cmlenz
Comment:

Use Include macro to pull reST doc from the repos

Legend:

Unmodified
Added
Removed
Modified
  • MarkupTemplates

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