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@226: rendered, and template expressions_ that are dynamically substituted by
cmlenz@226: variable data.
cmlenz@226:
cmlenz@226:
cmlenz@226: .. contents:: Contents
cmlenz@226: :depth: 3
cmlenz@226: .. sectnum::
cmlenz@226:
cmlenz@226: ----------
cmlenz@226: Python API
cmlenz@226: ----------
cmlenz@226:
cmlenz@230: The Python code required for templating with Genshi is generally based on the
cmlenz@226: following pattern:
cmlenz@226:
cmlenz@241: * Attain a ``MarkupTemplate`` object from a string or file object containing
cmlenz@241: the template XML source. This can either be done directly, or through a
cmlenz@226: ``TemplateLoader`` instance.
cmlenz@226: * Call the ``generate()`` method of the template, passing any data that should
cmlenz@226: be made available to the template as keyword arguments.
cmlenz@226: * Serialize the resulting stream using its ``render()`` method.
cmlenz@226:
cmlenz@226: For example::
cmlenz@226:
cmlenz@241: from genshi.template import MarkupTemplate
cmlenz@226:
cmlenz@241: tmpl = MarkupTemplate('
cmlenz@244:
cmlenz@244: You **will** however get a ``NameError`` if you try to call an undefined
cmlenz@244: variable, or do anything else with it, such as accessing its attributes. If you
cmlenz@244: need to know whether a variable is defined, you can check its type against the
cmlenz@354: ``Undefined`` class, for example in a `py:if`_ directive::
cmlenz@244:
cmlenz@244: >>> from genshi.template import TextTemplate
cmlenz@244: >>> tmpl = TextTemplate('${type(doh) is Undefined}')
cmlenz@244: >>> print tmpl.generate()
cmlenz@244: True
cmlenz@244:
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@394: To use directives in a template, the namespace must be declared, which is
cmlenz@226: usually done on the root element::
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@226: both ways::
cmlenz@226:
cmlenz@226:
cmlenz@226: ...
cmlenz@226:
cmlenz@226:
Bar
cmlenz@226:
cmlenz@226: ...
cmlenz@226:
cmlenz@226:
cmlenz@226: This is basically equivalent to the following::
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@235: The element is only rendered if the expression evaluates to a truth value::
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@235: would produce::
cmlenz@235:
cmlenz@235:
cmlenz@235: Hello
cmlenz@235:
cmlenz@235:
cmlenz@235: This directive can also be used as an element::
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@404: 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@404: 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@226: be tested for truth::
cmlenz@226:
cmlenz@226:
cmlenz@226: 0
cmlenz@226: 1
cmlenz@226: 2
cmlenz@226:
cmlenz@226:
cmlenz@226: This would produce the following output::
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@226: directives will be tested for equality to the parent ``py:choose`` value::
cmlenz@226:
cmlenz@226:
cmlenz@226: 0
cmlenz@226: 1
cmlenz@226: 2
cmlenz@226:
cmlenz@226:
cmlenz@226: This would produce the following output::
cmlenz@226:
cmlenz@226:
cmlenz@226: 1
cmlenz@226:
cmlenz@226:
cmlenz@226:
cmlenz@235: Looping
cmlenz@237: =======
cmlenz@226:
cmlenz@235: .. _`py:for`:
cmlenz@226:
cmlenz@235: ``py:for``
cmlenz@237: ----------
cmlenz@235:
cmlenz@235: The element is repeated for every item in an iterable::
cmlenz@226:
cmlenz@226:
cmlenz@235: - ${item}
cmlenz@226:
cmlenz@226:
cmlenz@235: Given ``items=[1, 2, 3]`` in the context data, this would produce::
cmlenz@226:
cmlenz@226:
cmlenz@235: - 1
- 2
- 3
cmlenz@226:
cmlenz@226:
cmlenz@235: This directive can also be used as an element::
cmlenz@226:
cmlenz@235:
cmlenz@235:
cmlenz@235: - ${item}
cmlenz@235:
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@226: inserted in other places::
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@226: The above would be rendered to::
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@394: If a macro doesn't require parameters, it can be defined without the
cmlenz@394: parenthesis. For example::
cmlenz@226:
cmlenz@226:
cmlenz@226:
cmlenz@226: Hello, world!
cmlenz@226:
cmlenz@394: ${greeting()}
cmlenz@226:
cmlenz@226:
cmlenz@226: The above would be rendered to::
cmlenz@226:
cmlenz@226:
cmlenz@226:
cmlenz@226: Hello, world!
cmlenz@226:
cmlenz@226:
cmlenz@226:
cmlenz@226: This directive can also be used as an element::
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@226: element with the tag name “greeting”::
cmlenz@226:
cmlenz@226:
cmlenz@226:
cmlenz@226: Hello ${select('@name')}
cmlenz@226:
cmlenz@226:
cmlenz@226:
cmlenz@226:
cmlenz@226: This would result in the following output::
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@230: in the output of the match template. See [wiki:GenshiStream#UsingXPath] for
cmlenz@226: more information about this function.
cmlenz@226:
cmlenz@226: This directive can also be used as an element::
cmlenz@226:
cmlenz@226:
cmlenz@226:
cmlenz@226: Hello ${select('@name')}
cmlenz@226:
cmlenz@226:
cmlenz@226:
cmlenz@226:
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@226: For example::
cmlenz@226:
cmlenz@226:
cmlenz@226: $x $y $z
cmlenz@226:
cmlenz@226:
cmlenz@226: Given ``x=42`` in the context data, this would produce::
cmlenz@226:
cmlenz@226:
cmlenz@226: 42 7 52
cmlenz@226:
cmlenz@226:
cmlenz@226: This directive can also be used as an element::
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@235: This directive adds, modifies or removes attributes from the element::
cmlenz@235:
cmlenz@235:
cmlenz@235: - Bar
cmlenz@235:
cmlenz@235:
cmlenz@235: Given ``foo={'class': 'collapse'}`` in the template context, this would
cmlenz@235: produce::
cmlenz@235:
cmlenz@235:
cmlenz@235: - Bar
cmlenz@235:
cmlenz@235:
cmlenz@235: Attributes with the value ``None`` are omitted, so given ``foo={'class': None}``
cmlenz@235: in the context for the same template this would produce::
cmlenz@235:
cmlenz@235:
cmlenz@235: - Bar
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@235: expression::
cmlenz@235:
cmlenz@235:
cmlenz@235: - Hello
cmlenz@235:
cmlenz@235:
cmlenz@235: Given ``bar='Bye'`` in the context data, this would produce::
cmlenz@235:
cmlenz@235:
cmlenz@235: - Bye
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@235: expression::
cmlenz@235:
cmlenz@235:
cmlenz@235: Hello
cmlenz@235:
cmlenz@235:
cmlenz@235: Given ``bar='Bye'`` in the context data, this would produce::
cmlenz@235:
cmlenz@235:
cmlenz@235: Bye
cmlenz@235:
cmlenz@235:
cmlenz@235: This directive can only be used as an attribute.
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@235: stripped from the output::
cmlenz@235:
cmlenz@235:
cmlenz@235:
foo
cmlenz@235:
cmlenz@235:
cmlenz@235: This would be rendered as::
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@226: file to be pulled in::
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@273: write::
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@230: Incudes in Genshi are fully dynamic: Just like normal attributes, the `href`
cmlenz@226: attribute accepts expressions_, and directives_ can be used on the
cmlenz@226: ```` element just as on any other element, meaning you can do
cmlenz@226: things like conditional includes::
cmlenz@226:
cmlenz@226:
cmlenz@226:
cmlenz@226:
cmlenz@226: .. _comments:
cmlenz@226:
cmlenz@226: --------
cmlenz@226: Comments
cmlenz@226: --------
cmlenz@226:
cmlenz@226: Normal XML/HTML comment syntax can be used in templates::
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@226: text with an exclamation mark::
cmlenz@226:
cmlenz@226:
cmlenz@226:
cmlenz@226: Note that it does not matter whether there's whitespace before or after the
cmlenz@226: exclamation mark, so the above could also be written as follows::
cmlenz@226:
cmlenz@226: