cmlenz@226: .. -*- mode: rst; encoding: utf-8 -*- cmlenz@226: cmlenz@226: ============================ cmlenz@230: Genshi XML Template Language cmlenz@226: ============================ cmlenz@226: cmlenz@241: Genshi provides a XML-based template language that is heavily inspired by Kid_, cmlenz@241: which in turn was inspired by a number of existing template languages, namely cmlenz@241: XSLT_, TAL_, and PHP_. cmlenz@226: cmlenz@226: .. _kid: http://kid-templating.org/ cmlenz@226: .. _python: http://www.python.org/ cmlenz@226: .. _xslt: http://www.w3.org/TR/xslt cmlenz@226: .. _tal: http://www.zope.org/Wikis/DevSite/Projects/ZPT/TAL cmlenz@226: .. _php: http://www.php.net/ cmlenz@226: cmlenz@226: This document describes the template language and will be most useful as cmlenz@241: reference to those developing Genshi XML templates. Templates are XML files of cmlenz@241: some kind (such as XHTML) that include processing directives_ (elements or cmlenz@226: attributes identified by a separate namespace) that affect how the template is cmlenz@500: rendered, and template expressions that are dynamically substituted by cmlenz@226: variable data. cmlenz@226: cmlenz@500: See `Genshi Templating Basics `_ for general information on cmlenz@500: embedding Python code in templates. cmlenz@500: cmlenz@226: cmlenz@226: .. contents:: Contents cmlenz@226: :depth: 3 cmlenz@226: .. sectnum:: cmlenz@226: cmlenz@226: cmlenz@226: .. _`directives`: cmlenz@226: cmlenz@226: ------------------- cmlenz@226: Template Directives cmlenz@226: ------------------- cmlenz@226: cmlenz@226: Directives are elements and/or attributes in the template that are identified cmlenz@230: by the namespace ``http://genshi.edgewall.org/``. They can affect how the cmlenz@230: template is rendered in a number of ways: Genshi provides directives for cmlenz@226: conditionals and looping, among others. cmlenz@226: cmlenz@395: To use directives in a template, the namespace must be declared, which is cmlenz@820: usually done on the root element: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@226: cmlenz@226: cmlenz@226: ... cmlenz@226: cmlenz@226: cmlenz@226: In this example, the default namespace is set to the XHTML namespace, and the cmlenz@230: namespace for Genshi directives is bound to the prefix “py”. cmlenz@226: cmlenz@226: All directives can be applied as attributes, and some can also be used as cmlenz@226: elements. The ``if`` directives for conditionals, for example, can be used in cmlenz@820: both ways: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@226: cmlenz@226: cmlenz@226: ... cmlenz@226:
cmlenz@226:

Bar

cmlenz@226:
cmlenz@226: ... cmlenz@226: cmlenz@226: cmlenz@820: This is basically equivalent to the following: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@226: cmlenz@226: cmlenz@226: ... cmlenz@226: cmlenz@226:
cmlenz@226:

Bar

cmlenz@226:
cmlenz@226:
cmlenz@226: ... cmlenz@226: cmlenz@226: cmlenz@226: The rationale behind the second form is that directives do not always map cmlenz@226: naturally to elements in the template. In such cases, the ``py:strip`` cmlenz@226: directive can be used to strip off the unwanted element, or the directive can cmlenz@226: simply be used as an element. cmlenz@226: cmlenz@226: cmlenz@237: Conditional Sections cmlenz@226: ==================== cmlenz@226: cmlenz@235: .. _`py:if`: cmlenz@226: cmlenz@235: ``py:if`` cmlenz@237: --------- cmlenz@226: cmlenz@820: The element and its content is only rendered if the expression evaluates to a cmlenz@820: truth value: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@226: cmlenz@235:
cmlenz@235: ${bar} cmlenz@235:
cmlenz@226: cmlenz@235: Given the data ``foo=True`` and ``bar='Hello'`` in the template context, this cmlenz@820: would produce: cmlenz@820: cmlenz@820: .. code-block:: xml cmlenz@235: cmlenz@235:
cmlenz@235: Hello cmlenz@235:
cmlenz@235: cmlenz@820: But setting ``foo=False`` would result in the following output: cmlenz@820: cmlenz@820: .. code-block:: xml cmlenz@820: cmlenz@820:
cmlenz@820:
cmlenz@820: cmlenz@820: This directive can also be used as an element: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@235: cmlenz@235:
cmlenz@235: cmlenz@235: ${bar} cmlenz@235: cmlenz@235:
cmlenz@226: cmlenz@226: .. _`py:choose`: cmlenz@226: .. _`py:when`: cmlenz@226: .. _`py:otherwise`: cmlenz@226: cmlenz@237: ``py:choose`` cmlenz@237: ------------- cmlenz@226: cmlenz@237: The ``py:choose`` directive, in combination with the directives ``py:when`` cmlenz@500: and ``py:otherwise`` provides advanced conditional processing for rendering one cmlenz@226: of several alternatives. The first matching ``py:when`` branch is rendered, or, cmlenz@500: if no ``py:when`` branch matches, the ``py:otherwise`` branch is rendered. cmlenz@226: cmlenz@226: If the ``py:choose`` directive is empty the nested ``py:when`` directives will cmlenz@820: be tested for truth: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@226: cmlenz@226:
cmlenz@226: 0 cmlenz@226: 1 cmlenz@226: 2 cmlenz@226:
cmlenz@226: cmlenz@820: This would produce the following output: cmlenz@820: cmlenz@820: .. code-block:: xml cmlenz@226: cmlenz@226:
cmlenz@226: 1 cmlenz@226:
cmlenz@226: cmlenz@226: If the ``py:choose`` directive contains an expression the nested ``py:when`` cmlenz@820: directives will be tested for equality to the parent ``py:choose`` value: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@226: cmlenz@226:
cmlenz@226: 0 cmlenz@226: 1 cmlenz@226: 2 cmlenz@226:
cmlenz@226: cmlenz@820: This would produce the following output: cmlenz@820: cmlenz@820: .. code-block:: xml cmlenz@226: cmlenz@226:
cmlenz@226: 1 cmlenz@226:
cmlenz@226: cmlenz@820: These directives can also be used as elements: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@820: cmlenz@820: cmlenz@820: 0 cmlenz@820: 1 cmlenz@820: 2 cmlenz@820: cmlenz@226: cmlenz@235: Looping cmlenz@237: ======= cmlenz@226: cmlenz@235: .. _`py:for`: cmlenz@226: cmlenz@235: ``py:for`` cmlenz@237: ---------- cmlenz@235: cmlenz@820: The element is repeated for every item in an iterable: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@226: cmlenz@226: cmlenz@226: cmlenz@820: Given ``items=[1, 2, 3]`` in the context data, this would produce: cmlenz@820: cmlenz@820: .. code-block:: xml cmlenz@226: cmlenz@226: cmlenz@226: cmlenz@820: This directive can also be used as an element: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@226: cmlenz@235: cmlenz@235: cmlenz@235: cmlenz@235: Snippet Reuse cmlenz@237: ============= cmlenz@226: cmlenz@226: .. _`py:def`: cmlenz@226: .. _`macros`: cmlenz@226: cmlenz@226: ``py:def`` cmlenz@237: ---------- cmlenz@226: cmlenz@226: The ``py:def`` directive can be used to create macros, i.e. snippets of cmlenz@226: template code that have a name and optionally some parameters, and that can be cmlenz@820: inserted in other places: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@226: cmlenz@226:
cmlenz@226:

cmlenz@226: Hello, ${name}! cmlenz@226:

cmlenz@226: ${greeting('world')} cmlenz@226: ${greeting('everyone else')} cmlenz@226:
cmlenz@226: cmlenz@820: The above would be rendered to: cmlenz@820: cmlenz@820: .. code-block:: xml cmlenz@226: cmlenz@226:
cmlenz@226:

cmlenz@226: Hello, world! cmlenz@226:

cmlenz@226:

cmlenz@226: Hello, everyone else! cmlenz@226:

cmlenz@226:
cmlenz@226: cmlenz@395: If a macro doesn't require parameters, it can be defined without the cmlenz@820: parenthesis. For example: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@226: cmlenz@226:
cmlenz@226:

cmlenz@226: Hello, world! cmlenz@226:

cmlenz@395: ${greeting()} cmlenz@226:
cmlenz@226: cmlenz@820: The above would be rendered to: cmlenz@820: cmlenz@820: .. code-block:: xml cmlenz@226: cmlenz@226:
cmlenz@226:

cmlenz@226: Hello, world! cmlenz@226:

cmlenz@226:
cmlenz@226: cmlenz@820: This directive can also be used as an element: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@226: cmlenz@226:
cmlenz@226: cmlenz@226:

Hello, ${name}!

cmlenz@226:
cmlenz@226:
cmlenz@226: cmlenz@226: cmlenz@235: .. _Match Templates: cmlenz@226: .. _`py:match`: cmlenz@226: cmlenz@226: ``py:match`` cmlenz@237: ------------ cmlenz@226: cmlenz@226: This directive defines a *match template*: given an XPath expression, it cmlenz@226: replaces any element in the template that matches the expression with its own cmlenz@226: content. cmlenz@226: cmlenz@226: For example, the match template defined in the following template matches any cmlenz@820: element with the tag name “greeting”: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@226: cmlenz@226:
cmlenz@226: cmlenz@226: Hello ${select('@name')} cmlenz@226: cmlenz@226: cmlenz@226:
cmlenz@226: cmlenz@820: This would result in the following output: cmlenz@820: cmlenz@820: .. code-block:: xml cmlenz@226: cmlenz@226:
cmlenz@226: cmlenz@226: Hello Dude cmlenz@226: cmlenz@226:
cmlenz@226: cmlenz@226: Inside the body of a ``py:match`` directive, the ``select(path)`` function is cmlenz@226: made available so that parts or all of the original element can be incorporated cmlenz@500: in the output of the match template. See `Using XPath`_ for more information cmlenz@500: about this function. cmlenz@500: cmlenz@500: .. _`Using XPath`: streams.html#using-xpath cmlenz@226: cmlenz@820: Match templates are applied both to the original markup as well to the cmlenz@820: generated markup. The order in which they are applied depends on the order cmlenz@820: they are declared in the template source: a match template defined after cmlenz@820: another match template is applied to the output generated by the first match cmlenz@820: template. The match templates basically form a pipeline. cmlenz@820: cmlenz@820: This directive can also be used as an element: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@226: cmlenz@226:
cmlenz@226: cmlenz@226: Hello ${select('@name')} cmlenz@226: cmlenz@226: cmlenz@226:
cmlenz@226: cmlenz@820: When used this way, the ``py:match`` directive can also be annotated with a cmlenz@820: couple of optimization hints. For example, the following informs the matching cmlenz@820: engine that the match should only be applied once: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@820: cmlenz@820: cmlenz@820: cmlenz@820: cmlenz@820: ${select("*|text()")} cmlenz@820: cmlenz@820: cmlenz@820: cmlenz@820: cmlenz@820: The following optimization hints are recognized: cmlenz@820: cmlenz@820: +---------------+-----------+-----------------------------------------------+ cmlenz@820: | Attribute | Default | Description | cmlenz@820: +===============+===========+===============================================+ cmlenz@820: | ``buffer`` | ``true`` | Whether the matched content should be | cmlenz@820: | | | buffered in memory. Buffering can improve | cmlenz@820: | | | performance a bit at the cost of needing more | cmlenz@820: | | | memory during rendering. Buffering is | cmlenz@820: | | | ''required'' for match templates that contain | cmlenz@820: | | | more than one invocation of the ``select()`` | cmlenz@820: | | | function. If there is only one call, and the | cmlenz@820: | | | matched content can potentially be very long, | cmlenz@820: | | | consider disabling buffering to avoid | cmlenz@820: | | | excessive memory use. | cmlenz@820: +---------------+-----------+-----------------------------------------------+ cmlenz@820: | ``once`` | ``false`` | Whether the engine should stop looking for | cmlenz@820: | | | more matching elements after the first match. | cmlenz@820: | | | Use this on match templates that match | cmlenz@820: | | | elements that can only occur once in the | cmlenz@820: | | | stream, such as the ```` or ```` | cmlenz@820: | | | elements in an HTML template, or elements | cmlenz@820: | | | with a specific ID. | cmlenz@820: +---------------+-----------+-----------------------------------------------+ cmlenz@820: | ``recursive`` | ``true`` | Whether the match template should be applied | cmlenz@820: | | | to its own output. Note that ``once`` implies | cmlenz@820: | | | non-recursive behavior, so this attribute | cmlenz@820: | | | only needs to be set for match templates that | cmlenz@820: | | | don't also have ``once`` set. | cmlenz@820: +---------------+-----------+-----------------------------------------------+ cmlenz@820: cmlenz@820: .. note:: The ``py:match`` optimization hints were added in the 0.5 release. In cmlenz@820: earlier versions, the attributes have no effect. cmlenz@820: cmlenz@226: cmlenz@235: Variable Binding cmlenz@237: ================ cmlenz@226: cmlenz@226: .. _`with`: cmlenz@226: cmlenz@226: ``py:with`` cmlenz@237: ----------- cmlenz@226: cmlenz@226: The ``py:with`` directive lets you assign expressions to variables, which can cmlenz@226: be used to make expressions inside the directive less verbose and more cmlenz@226: efficient. For example, if you need use the expression ``author.posts`` more cmlenz@226: than once, and that actually results in a database query, assigning the results cmlenz@226: to a variable using this directive would probably help. cmlenz@226: cmlenz@820: For example: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@226: cmlenz@226:
cmlenz@226: $x $y $z cmlenz@226:
cmlenz@226: cmlenz@820: Given ``x=42`` in the context data, this would produce: cmlenz@820: cmlenz@820: .. code-block:: xml cmlenz@226: cmlenz@226:
cmlenz@226: 42 7 52 cmlenz@226:
cmlenz@226: cmlenz@820: This directive can also be used as an element: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@226: cmlenz@226:
cmlenz@226: $x $y $z cmlenz@226:
cmlenz@226: cmlenz@226: Note that if a variable of the same name already existed outside of the scope cmlenz@226: of the ``py:with`` directive, it will **not** be overwritten. Instead, it cmlenz@226: will have the same value it had prior to the ``py:with`` assignment. cmlenz@230: Effectively, this means that variables are immutable in Genshi. cmlenz@226: cmlenz@226: cmlenz@235: Structure Manipulation cmlenz@237: ====================== cmlenz@235: cmlenz@235: .. _`py:attrs`: cmlenz@235: cmlenz@235: ``py:attrs`` cmlenz@237: ------------ cmlenz@235: cmlenz@820: This directive adds, modifies or removes attributes from the element: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@235: cmlenz@235: cmlenz@235: cmlenz@235: Given ``foo={'class': 'collapse'}`` in the template context, this would cmlenz@820: produce: cmlenz@820: cmlenz@820: .. code-block:: xml cmlenz@235: cmlenz@235: cmlenz@235: cmlenz@235: Attributes with the value ``None`` are omitted, so given ``foo={'class': None}`` cmlenz@820: in the context for the same template this would produce: cmlenz@820: cmlenz@820: .. code-block:: xml cmlenz@235: cmlenz@235: cmlenz@235: cmlenz@235: This directive can only be used as an attribute. cmlenz@235: cmlenz@235: cmlenz@235: .. _`py:content`: cmlenz@235: cmlenz@235: ``py:content`` cmlenz@237: -------------- cmlenz@235: cmlenz@235: This directive replaces any nested content with the result of evaluating the cmlenz@820: expression: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@235: cmlenz@235: cmlenz@235: cmlenz@820: Given ``bar='Bye'`` in the context data, this would produce: cmlenz@820: cmlenz@820: .. code-block:: xml cmlenz@235: cmlenz@235: cmlenz@235: cmlenz@235: This directive can only be used as an attribute. cmlenz@235: cmlenz@235: cmlenz@235: .. _`py:replace`: cmlenz@235: cmlenz@235: ``py:replace`` cmlenz@237: -------------- cmlenz@235: cmlenz@235: This directive replaces the element itself with the result of evaluating the cmlenz@820: expression: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@235: cmlenz@235:
cmlenz@235: Hello cmlenz@235:
cmlenz@235: cmlenz@820: Given ``bar='Bye'`` in the context data, this would produce: cmlenz@820: cmlenz@820: .. code-block:: xml cmlenz@235: cmlenz@235:
cmlenz@235: Bye cmlenz@235:
cmlenz@235: cmlenz@820: This directive can also be used as an element (since version 0.5): cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@820: cmlenz@820:
cmlenz@820: Placeholder cmlenz@820:
cmlenz@820: cmlenz@235: cmlenz@235: cmlenz@235: .. _`py:strip`: cmlenz@235: cmlenz@235: ``py:strip`` cmlenz@237: ------------ cmlenz@235: cmlenz@235: This directive conditionally strips the top-level element from the output. When cmlenz@235: the value of the ``py:strip`` attribute evaluates to ``True``, the element is cmlenz@820: stripped from the output: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@235: cmlenz@235:
cmlenz@235:
foo
cmlenz@235:
cmlenz@235: cmlenz@820: This would be rendered as: cmlenz@820: cmlenz@820: .. code-block:: xml cmlenz@235: cmlenz@235:
cmlenz@235: foo cmlenz@235:
cmlenz@235: cmlenz@235: As a shorthand, if the value of the ``py:strip`` attribute is empty, that has cmlenz@235: the same effect as using a truth value (i.e. the element is stripped). cmlenz@235: cmlenz@235: cmlenz@226: .. _order: cmlenz@226: cmlenz@226: Processing Order cmlenz@226: ================ cmlenz@226: cmlenz@226: It is possible to attach multiple directives to a single element, although not cmlenz@226: all combinations make sense. When multiple directives are encountered, they are cmlenz@226: processed in the following order: cmlenz@226: cmlenz@226: #. `py:def`_ cmlenz@226: #. `py:match`_ cmlenz@226: #. `py:when`_ cmlenz@226: #. `py:otherwise`_ cmlenz@226: #. `py:for`_ cmlenz@226: #. `py:if`_ cmlenz@226: #. `py:choose`_ cmlenz@226: #. `py:with`_ cmlenz@226: #. `py:replace`_ cmlenz@226: #. `py:content`_ cmlenz@226: #. `py:attrs`_ cmlenz@226: #. `py:strip`_ cmlenz@226: cmlenz@226: cmlenz@226: .. _includes: cmlenz@226: cmlenz@226: -------- cmlenz@226: Includes cmlenz@226: -------- cmlenz@226: cmlenz@226: To reuse common snippets of template code, you can include other files using cmlenz@226: XInclude_. cmlenz@226: cmlenz@226: .. _xinclude: http://www.w3.org/TR/xinclude/ cmlenz@226: cmlenz@226: For this, you need to declare the XInclude namespace (commonly bound to the cmlenz@226: prefix “xi”) and use the ```` element where you want the external cmlenz@820: file to be pulled in: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@226: cmlenz@226: cmlenz@226: cmlenz@226: ... cmlenz@226: cmlenz@226: cmlenz@226: Include paths are relative to the filename of the template currently being cmlenz@226: processed. So if the example above was in the file "``myapp/index.html``" cmlenz@226: (relative to the template search path), the XInclude processor would look for cmlenz@226: the included file at "``myapp/base.html``". You can also use Unix-style cmlenz@226: relative paths, for example "``../base.html``" to look in the parent directory. cmlenz@226: cmlenz@226: Any content included this way is inserted into the generated output instead of cmlenz@226: the ```` element. The included template sees the same context data. cmlenz@226: `Match templates`_ and `macros`_ in the included template are also available to cmlenz@226: the including template after the point it was included. cmlenz@226: cmlenz@226: By default, an error will be raised if an included file is not found. If that's cmlenz@226: not what you want, you can specify fallback content that should be used if the cmlenz@226: include fails. For example, to to make the include above fail silently, you'd cmlenz@820: write: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@226: cmlenz@226: cmlenz@226: cmlenz@273: See the `XInclude specification`_ for more about fallback content. Note though cmlenz@273: that Genshi currently only supports a small subset of XInclude. cmlenz@273: cmlenz@273: .. _`xinclude specification`: http://www.w3.org/TR/xinclude/ cmlenz@226: cmlenz@820: cmlenz@820: Dynamic Includes cmlenz@820: ================ cmlenz@820: cmlenz@230: Incudes in Genshi are fully dynamic: Just like normal attributes, the `href` cmlenz@500: attribute accepts expressions, and directives_ can be used on the cmlenz@226: ```` element just as on any other element, meaning you can do cmlenz@820: things like conditional includes: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@226: cmlenz@226: cmlenz@226: cmlenz@226: cmlenz@820: Including Text Templates cmlenz@820: ======================== cmlenz@820: cmlenz@820: The ``parse`` attribute of the ```` element can be used to specify cmlenz@820: whether the included template is an XML template or a text template (using the cmlenz@820: new syntax added in Genshi 0.5): cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@820: cmlenz@820: cmlenz@820: cmlenz@820: This example would load the ``myscript.js`` file as a ``NewTextTemplate``. See cmlenz@820: `text templates`_ for details on the syntax of text templates. cmlenz@820: cmlenz@820: .. _`text templates`: text-templates.html cmlenz@820: cmlenz@820: cmlenz@226: .. _comments: cmlenz@226: cmlenz@226: -------- cmlenz@226: Comments cmlenz@226: -------- cmlenz@226: cmlenz@820: Normal XML/HTML comment syntax can be used in templates: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@226: cmlenz@226: cmlenz@226: cmlenz@226: However, such comments get passed through the processing pipeline and are by cmlenz@226: default included in the final output. If that's not desired, prefix the comment cmlenz@820: text with an exclamation mark: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@226: cmlenz@226: cmlenz@226: cmlenz@226: Note that it does not matter whether there's whitespace before or after the cmlenz@820: exclamation mark, so the above could also be written as follows: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@226: cmlenz@226: