cmlenz@442: .. -*- mode: rst; encoding: utf-8 -*-
cmlenz@442:
cmlenz@442: ========================
cmlenz@442: Genshi Templating Basics
cmlenz@442: ========================
cmlenz@442:
cmlenz@442: Genshi provides a template engine that can be used for generating either
cmlenz@442: markup (such as HTML_ or XML_) or plain text. While both share some of the
cmlenz@442: syntax (and much of the underlying implementation) they are essentially
cmlenz@442: separate languages.
cmlenz@442:
cmlenz@442: .. _html: http://www.w3.org/html/
cmlenz@442: .. _xml: http://www.w3.org/XML/
cmlenz@442:
cmlenz@442: This document describes the common parts of the template engine and will be most
cmlenz@442: useful as reference to those developing Genshi templates. Templates are XML or
cmlenz@442: plain text files that include processing directives_ that affect how the
cmlenz@442: template is rendered, and template expressions_ that are dynamically substituted
cmlenz@442: by variable data.
cmlenz@442:
cmlenz@442:
cmlenz@442: .. contents:: Contents
cmlenz@442: :depth: 3
cmlenz@442: .. sectnum::
cmlenz@442:
cmlenz@442: --------
cmlenz@442: Synopsis
cmlenz@442: --------
cmlenz@442:
cmlenz@442: A Genshi *markup template* is a well-formed XML document with embedded Python
cmlenz@442: used for control flow and variable substitution. Markup templates should be
cmlenz@442: used to generate any kind of HTML or XML output, as they provide many advantages
cmlenz@442: over simple text-based templates (such as automatic escaping of strings).
cmlenz@442:
cmlenz@510: The following illustrates a very basic Genshi markup template:
cmlenz@510:
cmlenz@510: .. code-block:: genshi
cmlenz@442:
cmlenz@442:
cmlenz@442:
cmlenz@442:
cmlenz@442: This is replaced.
cmlenz@442:
cmlenz@442:
cmlenz@442:
cmlenz@442: These are some of my favorite fruits:
cmlenz@442:
cmlenz@442: -
cmlenz@442: I like ${fruit}s
cmlenz@442:
cmlenz@442:
cmlenz@442:
cmlenz@442:
cmlenz@442:
cmlenz@442: This example shows:
cmlenz@442:
cmlenz@442: (a) a Python code block, using a processing instruction
cmlenz@442: (b) the Genshi namespace declaration
cmlenz@442: (c) usage of templates directives (``py:content`` and ``py:for``)
cmlenz@442: (d) an inline Python expression (``${fruit}``).
cmlenz@442:
cmlenz@510: The template would generate output similar to this:
cmlenz@510:
cmlenz@510: .. code-block:: genshi
cmlenz@445:
cmlenz@445:
cmlenz@445:
cmlenz@445: A Genshi Template
cmlenz@445:
cmlenz@445:
cmlenz@445:
cmlenz@445: These are some of my favorite fruits:
cmlenz@445:
cmlenz@445: - I like apples
cmlenz@445: - I like oranges
cmlenz@445: - I like kiwis
cmlenz@445:
cmlenz@445:
cmlenz@445:
cmlenz@445:
cmlenz@442: A *text template* is a simple plain text document that can also contain embedded
cmlenz@442: Python code. Text templates can be used to generate simple *non-markup* text
cmlenz@510: formats, such as the body of an plain text email. For example:
cmlenz@510:
cmlenz@510: .. code-block:: genshitext
cmlenz@442:
cmlenz@442: Dear $name,
cmlenz@442:
cmlenz@442: These are some of my favorite fruits:
cmlenz@442: #for fruit in fruits
cmlenz@442: * $fruit
cmlenz@442: #end
cmlenz@442:
cmlenz@442:
cmlenz@442: ----------
cmlenz@442: Python API
cmlenz@442: ----------
cmlenz@442:
cmlenz@442: The Python code required for templating with Genshi is generally based on the
cmlenz@442: following pattern:
cmlenz@442:
cmlenz@442: * Attain a ``MarkupTemplate`` or ``TextTemplate`` object from a string or
cmlenz@442: file-like object containing the template source. This can either be done
cmlenz@442: directly, or through a ``TemplateLoader`` instance.
cmlenz@442: * Call the ``generate()`` method of the template, passing any data that should
cmlenz@442: be made available to the template as keyword arguments.
cmlenz@442: * Serialize the resulting stream using its ``render()`` method.
cmlenz@442:
cmlenz@510: For example:
cmlenz@510:
cmlenz@510: .. code-block:: pycon
cmlenz@442:
cmlenz@442: >>> from genshi.template import MarkupTemplate
cmlenz@442: >>> tmpl = MarkupTemplate('Hello, $name!
')
cmlenz@442: >>> stream = tmpl.generate(name='world')
cmlenz@592: >>> print stream.render('xhtml')
cmlenz@442: Hello, world!
cmlenz@442:
cmlenz@592: .. note:: See the Serialization_ section of the `Markup Streams`_ page for
cmlenz@592: information on configuring template output options.
cmlenz@592:
cmlenz@510: Using a text template is similar:
cmlenz@510:
cmlenz@510: .. code-block:: pycon
cmlenz@442:
cmlenz@442: >>> from genshi.template import TextTemplate
cmlenz@442: >>> tmpl = TextTemplate('Hello, $name!')
cmlenz@442: >>> stream = tmpl.generate(name='world')
cmlenz@605: >>> print stream
cmlenz@442: Hello, world!
cmlenz@442:
cmlenz@592: .. note:: If you want to use text templates, you should consider using the
cmlenz@592: ``NewTextTemplate`` class instead of simply ``TextTemplate``. See
cmlenz@592: the `Text Template Language`_ page.
cmlenz@442:
cmlenz@442: .. _serialization: streams.html#serialization
cmlenz@592: .. _`Text Template Language`: text-templates.html
cmlenz@442: .. _`Markup Streams`: streams.html
cmlenz@442:
cmlenz@442: Using a template loader provides the advantage that “compiled” templates are
cmlenz@442: automatically cached, and only parsed again when the template file changes. In
cmlenz@442: addition, it enables the use of a *template search path*, allowing template
cmlenz@442: directories to be spread across different file-system locations. Using a
cmlenz@510: template loader would generally look as follows:
cmlenz@510:
cmlenz@510: .. code-block:: python
cmlenz@442:
cmlenz@442: from genshi.template import TemplateLoader
cmlenz@442: loader = TemplateLoader([templates_dir1, templates_dir2])
cmlenz@442: tmpl = loader.load('test.html')
cmlenz@442: stream = tmpl.generate(title='Hello, world!')
cmlenz@442: print stream.render()
cmlenz@442:
cmlenz@442: See the `API documentation `_ for details on using Genshi via
cmlenz@442: the Python API.
cmlenz@442:
cmlenz@442:
cmlenz@442: .. _`expressions`:
cmlenz@442:
cmlenz@442: ------------------------------------
cmlenz@442: Template Expressions and Code Blocks
cmlenz@442: ------------------------------------
cmlenz@442:
cmlenz@442: Python_ expressions can be used in text and directive arguments. An expression
cmlenz@442: is substituted with the result of its evaluation against the template data.
cmlenz@442: Expressions in text (which includes the values of non-directive attributes) need
cmlenz@442: to prefixed with a dollar sign (``$``) and usually enclosed in curly braces
cmlenz@442: (``{…}``).
cmlenz@442:
cmlenz@442: .. _python: http://www.python.org/
cmlenz@442:
cmlenz@442: If the expression starts with a letter and contains only letters, digits, dots,
cmlenz@442: and underscores, the curly braces may be omitted. In all other cases, the
cmlenz@442: braces are required so that the template processor knows where the expression
cmlenz@510: ends:
cmlenz@510:
cmlenz@510: .. code-block:: pycon
cmlenz@442:
cmlenz@442: >>> from genshi.template import MarkupTemplate
cmlenz@442: >>> tmpl = MarkupTemplate('${items[0].capitalize()} item')
cmlenz@442: >>> print tmpl.generate(items=['first', 'second'])
cmlenz@442: First item
cmlenz@442:
cmlenz@442: Expressions support the full power of Python. In addition, it is possible to
cmlenz@442: access items in a dictionary using “dotted notation” (i.e. as if they were
cmlenz@442: attributes), and vice-versa (i.e. access attributes as if they were items in a
cmlenz@510: dictionary):
cmlenz@510:
cmlenz@510: .. code-block:: pycon
cmlenz@442:
cmlenz@442: >>> from genshi.template import MarkupTemplate
cmlenz@442: >>> tmpl = MarkupTemplate('${dict.foo}')
cmlenz@442: >>> print tmpl.generate(dict={'foo': 'bar'})
cmlenz@442: bar
cmlenz@442:
cmlenz@442: Because there are two ways to access either attributes or items, expressions
cmlenz@442: do not raise the standard ``AttributeError`` or ``IndexError`` exceptions, but
cmlenz@442: rather an exception of the type ``UndefinedError``. The same kind of error is
cmlenz@476: raised when you try to use a top-level variable that is not in the context data.
cmlenz@476: See `Error Handling`_ below for details on how such errors are handled.
cmlenz@442:
cmlenz@442:
cmlenz@442: .. _`code blocks`:
cmlenz@442:
cmlenz@442: Code Blocks
cmlenz@442: ===========
cmlenz@442:
cmlenz@609: Templates also support full Python code blocks, using the ````
cmlenz@609: processing instruction in XML templates:
cmlenz@510:
cmlenz@510: .. code-block:: genshi
cmlenz@442:
cmlenz@442:
cmlenz@442:
cmlenz@442: ${greeting('world')}
cmlenz@442:
cmlenz@442:
cmlenz@510: This will produce the following output:
cmlenz@510:
cmlenz@609: .. code-block:: xml
cmlenz@442:
cmlenz@442:
cmlenz@442: Hello, world!
cmlenz@442:
cmlenz@442:
cmlenz@609: In text templates (although only those using the new syntax introduced in
cmlenz@609: Genshi 0.5), code blocks use the special ``{% python %}`` directive:
cmlenz@609:
cmlenz@609: .. code-block:: genshitext
cmlenz@609:
cmlenz@609: {% python
cmlenz@609: from genshi.builder import tag
cmlenz@609: def greeting(name):
cmlenz@649: return 'Hello, %s!' % name
cmlenz@609: %}
cmlenz@609: ${greeting('world')}
cmlenz@609:
cmlenz@609: This will produce the following output::
cmlenz@609:
cmlenz@609: Hello, world!
cmlenz@609:
cmlenz@609:
cmlenz@442: Code blocks can import modules, define classes and functions, and basically do
cmlenz@442: anything you can do in normal Python code. What code blocks can *not* do is to
cmlenz@606: produce content that is emitted directly tp the generated output.
cmlenz@442:
cmlenz@442: .. note:: Using the ``print`` statement will print to the standard output
cmlenz@442: stream, just as it does for other Python code in your application.
cmlenz@442:
cmlenz@476: Unlike expressions, Python code in ```` processing instructions can
cmlenz@476: not use item and attribute access in an interchangeable manner. That means that
cmlenz@476: “dotted notation” is always attribute access, and vice-versa.
cmlenz@476:
cmlenz@476: The support for Python code blocks in templates is not supposed to encourage
cmlenz@476: mixing application code into templates, which is generally considered bad
cmlenz@476: design. If you're using many code blocks, that may be a sign that you should
cmlenz@476: move such code into separate Python modules.
cmlenz@442:
cmlenz@545: If you'd rather not allow the use of Python code blocks in templates, you can
cmlenz@545: simply set the ``allow_exec`` parameter (available on the ``Template`` and the
cmlenz@545: ``TemplateLoader`` initializers) to ``False``. In that case Genshi will raise
cmlenz@545: a syntax error when a ```` processing instruction is encountered.
cmlenz@545: But please note that disallowing code blocks in templates does not turn Genshi
cmlenz@545: into a sandboxable template engine; there are sufficient ways to do harm even
cmlenz@545: using plain expressions.
cmlenz@545:
aflett@703: .. warning:: Unfortunately, code blocks are severely limited when running
aflett@703: under Python 2.3: For example, it is not possible to access
aflett@703: variables defined in outer scopes. If you plan to use code blocks
aflett@703: extensively, it is strongly recommended that you run Python 2.4
aflett@703: or later.
aflett@703:
cmlenz@442:
cmlenz@442: .. _`error handling`:
cmlenz@442:
cmlenz@442: Error Handling
cmlenz@442: ==============
cmlenz@442:
cmlenz@606: By default, Genshi raises an ``UndefinedError`` if a template expression
cmlenz@606: attempts to access a variable that is not defined:
cmlenz@510:
cmlenz@510: .. code-block:: pycon
cmlenz@442:
cmlenz@442: >>> from genshi.template import MarkupTemplate
cmlenz@442: >>> tmpl = MarkupTemplate('${doh}
')
cmlenz@606: >>> tmpl.generate().render('xhtml')
cmlenz@606: Traceback (most recent call last):
cmlenz@606: ...
cmlenz@606: UndefinedError: "doh" not defined
cmlenz@606:
cmlenz@606: You can change this behavior by setting the variable lookup mode to "lenient".
cmlenz@606: In that case, accessing undefined variables returns an `Undefined` object,
cmlenz@606: meaning that the expression does not fail immediately. See below for details.
cmlenz@606:
cmlenz@606: If you need to check whether a variable exists in the template context, use the
cmlenz@606: defined_ or the value_of_ function described below. To check for existence of
cmlenz@606: attributes on an object, or keys in a dictionary, use the ``hasattr()``,
cmlenz@606: ``getattr()`` or ``get()`` functions, or the ``in`` operator, just as you would
cmlenz@606: in regular Python code:
cmlenz@606:
cmlenz@606: >>> from genshi.template import MarkupTemplate
cmlenz@606: >>> tmpl = MarkupTemplate('${defined("doh")}
')
cmlenz@606: >>> print tmpl.generate().render('xhtml')
cmlenz@606: False
cmlenz@606:
cmlenz@606: .. note:: Lenient error handling was the default in Genshi prior to version 0.5.
cmlenz@606: Strict mode was introduced in version 0.4, and became the default in
cmlenz@606: 0.5. The reason for this change was that the lenient error handling
cmlenz@606: was masking actual errors in templates, thereby also making it harder
cmlenz@606: to debug some problems.
cmlenz@606:
cmlenz@606:
cmlenz@606: .. _`lenient`:
cmlenz@606:
cmlenz@606: Lenient Mode
cmlenz@606: ------------
cmlenz@606:
cmlenz@606: If you instruct Genshi to use the lenient variable lookup mode, it allows you
cmlenz@606: to access variables that are not defined, without raising an ``UndefinedError``.
cmlenz@606:
cmlenz@606: This mode can be chosen by passing the ``lookup='lenient'`` keyword argument to
cmlenz@606: the template initializer, or by passing the ``variable_lookup='lenient'``
cmlenz@606: keyword argument to the ``TemplateLoader`` initializer:
cmlenz@606:
cmlenz@606: .. code-block:: pycon
cmlenz@606:
cmlenz@606: >>> from genshi.template import MarkupTemplate
cmlenz@606: >>> tmpl = MarkupTemplate('${doh}
', lookup='lenient')
cmlenz@442: >>> print tmpl.generate().render('xhtml')
cmlenz@442:
cmlenz@442:
cmlenz@442: You *will* however get an exception if you try to call an undefined variable, or
cmlenz@510: do anything else with it, such as accessing its attributes:
cmlenz@510:
cmlenz@510: .. code-block:: pycon
cmlenz@442:
cmlenz@442: >>> from genshi.template import MarkupTemplate
cmlenz@606: >>> tmpl = MarkupTemplate('${doh.oops}
', lookup='lenient')
cmlenz@442: >>> print tmpl.generate().render('xhtml')
cmlenz@442: Traceback (most recent call last):
cmlenz@442: ...
cmlenz@442: UndefinedError: "doh" not defined
cmlenz@442:
cmlenz@442: If you need to know whether a variable is defined, you can check its type
cmlenz@510: against the ``Undefined`` class, for example in a conditional directive:
cmlenz@510:
cmlenz@510: .. code-block:: pycon
cmlenz@442:
cmlenz@442: >>> from genshi.template import MarkupTemplate
cmlenz@606: >>> tmpl = MarkupTemplate('${type(doh) is not Undefined}
',
cmlenz@606: ... lookup='lenient')
cmlenz@442: >>> print tmpl.generate().render('xhtml')
cmlenz@442: False
cmlenz@442:
cmlenz@442: Alternatively, the built-in functions defined_ or value_of_ can be used in this
cmlenz@442: case.
cmlenz@442:
cmlenz@442: Custom Modes
cmlenz@442: ------------
cmlenz@442:
cmlenz@442: In addition to the built-in "lenient" and "strict" modes, it is also possible to
cmlenz@442: use a custom error handling mode. For example, you could use lenient error
cmlenz@442: handling in a production environment, while also logging a warning when an
cmlenz@442: undefined variable is referenced.
cmlenz@442:
cmlenz@442: See the API documentation of the ``genshi.template.eval`` module for details.
cmlenz@442:
cmlenz@442:
cmlenz@442: Built-in Functions & Types
cmlenz@442: ==========================
cmlenz@442:
cmlenz@442: The following functions and types are available by default in template code, in
cmlenz@442: addition to the standard built-ins that are available to all Python code.
cmlenz@442:
cmlenz@442: .. _`defined`:
cmlenz@442:
cmlenz@442: ``defined(name)``
cmlenz@442: -----------------
cmlenz@442: This function determines whether a variable of the specified name exists in
cmlenz@442: the context data, and returns ``True`` if it does.
cmlenz@442:
cmlenz@442: .. _`value_of`:
cmlenz@442:
cmlenz@442: ``value_of(name, default=None)``
cmlenz@442: --------------------------------
cmlenz@442: This function returns the value of the variable with the specified name if
cmlenz@442: such a variable is defined, and returns the value of the ``default``
cmlenz@442: parameter if no such variable is defined.
cmlenz@442:
cmlenz@442: .. _`Markup`:
cmlenz@442:
cmlenz@442: ``Markup(text)``
cmlenz@442: ----------------
cmlenz@442: The ``Markup`` type marks a given string as being safe for inclusion in markup,
cmlenz@442: meaning it will *not* be escaped in the serialization stage. Use this with care,
cmlenz@442: as not escaping a user-provided string may allow malicious users to open your
cmlenz@442: web site to cross-site scripting attacks.
cmlenz@442:
cmlenz@442: .. _`Undefined`:
cmlenz@442:
cmlenz@442: ``Undefined``
cmlenz@442: ----------------
cmlenz@442: The ``Undefined`` type can be used to check whether a reference variable is
cmlenz@442: defined, as explained in `error handling`_.
cmlenz@442:
cmlenz@442:
cmlenz@442: .. _`directives`:
cmlenz@442:
cmlenz@442: -------------------
cmlenz@442: Template Directives
cmlenz@442: -------------------
cmlenz@442:
cmlenz@442: Directives provide control flow functionality for templates, such as conditions
cmlenz@442: or iteration. As the syntax for directives depends on whether you're using
cmlenz@442: markup or text templates, refer to the
cmlenz@442: `XML Template Language `_ or
cmlenz@442: `Text Template Language `_ pages for information.