cmlenz@500: .. -*- mode: rst; encoding: utf-8 -*- cmlenz@500: cmlenz@500: ======================== cmlenz@500: Genshi Templating Basics cmlenz@500: ======================== cmlenz@500: cmlenz@500: Genshi provides a template engine that can be used for generating either cmlenz@500: markup (such as HTML_ or XML_) or plain text. While both share some of the cmlenz@500: syntax (and much of the underlying implementation) they are essentially cmlenz@500: separate languages. cmlenz@500: cmlenz@500: .. _html: http://www.w3.org/html/ cmlenz@500: .. _xml: http://www.w3.org/XML/ cmlenz@500: cmlenz@500: This document describes the common parts of the template engine and will be most cmlenz@500: useful as reference to those developing Genshi templates. Templates are XML or cmlenz@500: plain text files that include processing directives_ that affect how the cmlenz@500: template is rendered, and template expressions_ that are dynamically substituted cmlenz@500: by variable data. cmlenz@500: cmlenz@500: cmlenz@500: .. contents:: Contents cmlenz@500: :depth: 3 cmlenz@500: .. sectnum:: cmlenz@500: cmlenz@500: -------- cmlenz@500: Synopsis cmlenz@500: -------- cmlenz@500: cmlenz@500: A Genshi *markup template* is a well-formed XML document with embedded Python cmlenz@500: used for control flow and variable substitution. Markup templates should be cmlenz@902: used to generate any kind of HTML or XML output, as they provide a number of cmlenz@902: advantages over simple text-based templates (such as automatic escaping of cmlenz@902: variable data). cmlenz@500: cmlenz@902: The following is a simple Genshi markup template: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@500: cmlenz@500: cmlenz@500: cmlenz@500: cmlenz@500: This is replaced. cmlenz@500: cmlenz@500: cmlenz@500: cmlenz@500:

These are some of my favorite fruits:

cmlenz@500: cmlenz@500: cmlenz@500: cmlenz@500: cmlenz@500: This example shows: cmlenz@500: cmlenz@902: (a) a Python code block in a processing instruction cmlenz@500: (b) the Genshi namespace declaration cmlenz@500: (c) usage of templates directives (``py:content`` and ``py:for``) cmlenz@500: (d) an inline Python expression (``${fruit}``). cmlenz@500: cmlenz@820: The template would generate output similar to this: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@500: cmlenz@500: cmlenz@500: cmlenz@500: A Genshi Template cmlenz@500: cmlenz@500: cmlenz@500: cmlenz@500:

These are some of my favorite fruits:

cmlenz@500: cmlenz@500: cmlenz@500: cmlenz@500: cmlenz@902: A *text template* is a simple plain text document that can also contain cmlenz@902: embedded Python code. Text templates are intended to be used for simple cmlenz@902: *non-markup* text formats, such as the body of an plain text email. For cmlenz@902: example: cmlenz@820: cmlenz@820: .. code-block:: genshitext cmlenz@500: cmlenz@500: Dear $name, cmlenz@500: cmlenz@500: These are some of my favorite fruits: cmlenz@500: #for fruit in fruits cmlenz@500: * $fruit cmlenz@500: #end cmlenz@500: cmlenz@500: cmlenz@500: ---------- cmlenz@500: Python API cmlenz@500: ---------- cmlenz@500: cmlenz@500: The Python code required for templating with Genshi is generally based on the cmlenz@500: following pattern: cmlenz@500: cmlenz@500: * Attain a ``MarkupTemplate`` or ``TextTemplate`` object from a string or cmlenz@500: file-like object containing the template source. This can either be done cmlenz@500: directly, or through a ``TemplateLoader`` instance. cmlenz@500: * Call the ``generate()`` method of the template, passing any data that should cmlenz@500: be made available to the template as keyword arguments. cmlenz@500: * Serialize the resulting stream using its ``render()`` method. cmlenz@500: cmlenz@820: For example: cmlenz@820: cmlenz@820: .. code-block:: pycon cmlenz@500: cmlenz@500: >>> from genshi.template import MarkupTemplate cmlenz@500: >>> tmpl = MarkupTemplate('

Hello, $name!

') cmlenz@500: >>> stream = tmpl.generate(name='world') cmlenz@902: >>> print(stream.render('xhtml')) cmlenz@500:

Hello, world!

cmlenz@500: cmlenz@820: .. note:: See the Serialization_ section of the `Markup Streams`_ page for cmlenz@820: information on configuring template output options. cmlenz@820: cmlenz@820: Using a text template is similar: cmlenz@820: cmlenz@820: .. code-block:: pycon cmlenz@500: cmlenz@500: >>> from genshi.template import TextTemplate cmlenz@500: >>> tmpl = TextTemplate('Hello, $name!') cmlenz@500: >>> stream = tmpl.generate(name='world') cmlenz@902: >>> print(stream) cmlenz@500: Hello, world! cmlenz@500: cmlenz@820: .. note:: If you want to use text templates, you should consider using the cmlenz@820: ``NewTextTemplate`` class instead of simply ``TextTemplate``. See cmlenz@820: the `Text Template Language`_ page. cmlenz@500: cmlenz@500: .. _serialization: streams.html#serialization cmlenz@820: .. _`Text Template Language`: text-templates.html cmlenz@500: .. _`Markup Streams`: streams.html cmlenz@500: cmlenz@902: Using a `template loader`_ provides the advantage that “compiled” templates are cmlenz@500: automatically cached, and only parsed again when the template file changes. In cmlenz@500: addition, it enables the use of a *template search path*, allowing template cmlenz@500: directories to be spread across different file-system locations. Using a cmlenz@820: template loader would generally look as follows: cmlenz@820: cmlenz@820: .. code-block:: python cmlenz@500: cmlenz@500: from genshi.template import TemplateLoader cmlenz@500: loader = TemplateLoader([templates_dir1, templates_dir2]) cmlenz@500: tmpl = loader.load('test.html') cmlenz@500: stream = tmpl.generate(title='Hello, world!') cmlenz@902: print(stream.render()) cmlenz@500: cmlenz@500: See the `API documentation `_ for details on using Genshi via cmlenz@500: the Python API. cmlenz@500: cmlenz@902: .. _`template loader`: loader.html cmlenz@500: cmlenz@500: .. _`expressions`: cmlenz@500: cmlenz@500: ------------------------------------ cmlenz@500: Template Expressions and Code Blocks cmlenz@500: ------------------------------------ cmlenz@500: cmlenz@500: Python_ expressions can be used in text and directive arguments. An expression cmlenz@500: is substituted with the result of its evaluation against the template data. cmlenz@500: Expressions in text (which includes the values of non-directive attributes) need cmlenz@500: to prefixed with a dollar sign (``$``) and usually enclosed in curly braces cmlenz@500: (``{…}``). cmlenz@500: cmlenz@500: .. _python: http://www.python.org/ cmlenz@500: cmlenz@500: If the expression starts with a letter and contains only letters, digits, dots, cmlenz@500: and underscores, the curly braces may be omitted. In all other cases, the cmlenz@500: braces are required so that the template processor knows where the expression cmlenz@820: ends: cmlenz@820: cmlenz@820: .. code-block:: pycon cmlenz@500: cmlenz@500: >>> from genshi.template import MarkupTemplate cmlenz@500: >>> tmpl = MarkupTemplate('${items[0].capitalize()} item') cmlenz@902: >>> print(tmpl.generate(items=['first', 'second'])) cmlenz@500: First item cmlenz@500: cmlenz@500: Expressions support the full power of Python. In addition, it is possible to cmlenz@500: access items in a dictionary using “dotted notation” (i.e. as if they were cmlenz@500: attributes), and vice-versa (i.e. access attributes as if they were items in a cmlenz@820: dictionary): cmlenz@820: cmlenz@820: .. code-block:: pycon cmlenz@500: cmlenz@500: >>> from genshi.template import MarkupTemplate cmlenz@500: >>> tmpl = MarkupTemplate('${dict.foo}') cmlenz@902: >>> print(tmpl.generate(dict={'foo': 'bar'})) cmlenz@500: bar cmlenz@500: cmlenz@500: Because there are two ways to access either attributes or items, expressions cmlenz@500: do not raise the standard ``AttributeError`` or ``IndexError`` exceptions, but cmlenz@500: rather an exception of the type ``UndefinedError``. The same kind of error is cmlenz@500: raised when you try to use a top-level variable that is not in the context data. cmlenz@500: See `Error Handling`_ below for details on how such errors are handled. cmlenz@500: cmlenz@500: cmlenz@820: Escaping cmlenz@820: ======== cmlenz@820: cmlenz@820: If you need to include a literal dollar sign in the output where Genshi would cmlenz@820: normally detect an expression, you can simply add another dollar sign: cmlenz@820: cmlenz@820: .. code-block:: pycon cmlenz@820: cmlenz@820: >>> from genshi.template import MarkupTemplate cmlenz@820: >>> tmpl = MarkupTemplate('$foo') # Wanted "$foo" as literal output cmlenz@902: >>> print(tmpl.generate()) cmlenz@820: Traceback (most recent call last): cmlenz@820: ... cmlenz@820: UndefinedError: "foo" not defined cmlenz@820: >>> tmpl = MarkupTemplate('$$foo') cmlenz@902: >>> print(tmpl.generate()) cmlenz@820: $foo cmlenz@820: cmlenz@820: But note that this is not necessary if the characters following the dollar sign cmlenz@820: do not qualify as an expression. For example, the following needs no escaping: cmlenz@820: cmlenz@820: .. code-block:: pycon cmlenz@820: cmlenz@820: >>> tmpl = MarkupTemplate('') cmlenz@902: >>> print(tmpl.generate()) cmlenz@820: cmlenz@820: cmlenz@820: On the other hand, Genshi will always replace two dollar signs in text with a cmlenz@820: single dollar sign, so you'll need to use three dollar signs to get two in the cmlenz@820: output: cmlenz@820: cmlenz@820: .. code-block:: pycon cmlenz@820: cmlenz@820: >>> tmpl = MarkupTemplate('') cmlenz@902: >>> print(tmpl.generate()) cmlenz@820: cmlenz@820: cmlenz@820: cmlenz@500: .. _`code blocks`: cmlenz@500: cmlenz@500: Code Blocks cmlenz@500: =========== cmlenz@500: cmlenz@820: Templates also support full Python code blocks, using the ```` cmlenz@820: processing instruction in XML templates: cmlenz@820: cmlenz@820: .. code-block:: genshi cmlenz@500: cmlenz@500:
cmlenz@500: cmlenz@500: ${greeting('world')} cmlenz@500:
cmlenz@500: cmlenz@820: This will produce the following output: cmlenz@820: cmlenz@820: .. code-block:: xml cmlenz@500: cmlenz@500:
cmlenz@500: Hello, world! cmlenz@500:
cmlenz@500: cmlenz@820: In text templates (although only those using the new syntax introduced in cmlenz@820: Genshi 0.5), code blocks use the special ``{% python %}`` directive: cmlenz@820: cmlenz@820: .. code-block:: genshitext cmlenz@820: cmlenz@820: {% python cmlenz@820: from genshi.builder import tag cmlenz@820: def greeting(name): cmlenz@820: return 'Hello, %s!' % name cmlenz@820: %} cmlenz@820: ${greeting('world')} cmlenz@820: cmlenz@820: This will produce the following output:: cmlenz@820: cmlenz@820: Hello, world! cmlenz@820: cmlenz@820: cmlenz@500: Code blocks can import modules, define classes and functions, and basically do cmlenz@500: anything you can do in normal Python code. What code blocks can *not* do is to cmlenz@820: produce content that is emitted directly tp the generated output. cmlenz@500: cmlenz@500: .. note:: Using the ``print`` statement will print to the standard output cmlenz@500: stream, just as it does for other Python code in your application. cmlenz@500: cmlenz@500: Unlike expressions, Python code in ```` processing instructions can cmlenz@500: not use item and attribute access in an interchangeable manner. That means that cmlenz@500: “dotted notation” is always attribute access, and vice-versa. cmlenz@500: cmlenz@500: The support for Python code blocks in templates is not supposed to encourage cmlenz@500: mixing application code into templates, which is generally considered bad cmlenz@500: design. If you're using many code blocks, that may be a sign that you should cmlenz@500: move such code into separate Python modules. cmlenz@500: cmlenz@820: If you'd rather not allow the use of Python code blocks in templates, you can cmlenz@820: simply set the ``allow_exec`` parameter (available on the ``Template`` and the cmlenz@820: ``TemplateLoader`` initializers) to ``False``. In that case Genshi will raise cmlenz@820: a syntax error when a ```` processing instruction is encountered. cmlenz@820: But please note that disallowing code blocks in templates does not turn Genshi cmlenz@820: into a sandboxable template engine; there are sufficient ways to do harm even cmlenz@820: using plain expressions. cmlenz@500: cmlenz@500: cmlenz@500: .. _`error handling`: cmlenz@500: cmlenz@500: Error Handling cmlenz@500: ============== cmlenz@500: cmlenz@820: By default, Genshi raises an ``UndefinedError`` if a template expression cmlenz@820: attempts to access a variable that is not defined: cmlenz@820: cmlenz@820: .. code-block:: pycon cmlenz@500: cmlenz@500: >>> from genshi.template import MarkupTemplate cmlenz@500: >>> tmpl = MarkupTemplate('

${doh}

') cmlenz@820: >>> tmpl.generate().render('xhtml') cmlenz@820: Traceback (most recent call last): cmlenz@820: ... cmlenz@820: UndefinedError: "doh" not defined cmlenz@820: cmlenz@820: You can change this behavior by setting the variable lookup mode to "lenient". cmlenz@820: In that case, accessing undefined variables returns an `Undefined` object, cmlenz@820: meaning that the expression does not fail immediately. See below for details. cmlenz@820: cmlenz@820: If you need to check whether a variable exists in the template context, use the cmlenz@820: defined_ or the value_of_ function described below. To check for existence of cmlenz@820: attributes on an object, or keys in a dictionary, use the ``hasattr()``, cmlenz@820: ``getattr()`` or ``get()`` functions, or the ``in`` operator, just as you would cmlenz@820: in regular Python code: cmlenz@820: cmlenz@820: >>> from genshi.template import MarkupTemplate cmlenz@820: >>> tmpl = MarkupTemplate('

${defined("doh")}

') cmlenz@902: >>> print(tmpl.generate().render('xhtml')) cmlenz@820:

False

cmlenz@820: cmlenz@820: .. note:: Lenient error handling was the default in Genshi prior to version 0.5. cmlenz@820: Strict mode was introduced in version 0.4, and became the default in cmlenz@820: 0.5. The reason for this change was that the lenient error handling cmlenz@820: was masking actual errors in templates, thereby also making it harder cmlenz@820: to debug some problems. cmlenz@820: cmlenz@820: cmlenz@820: .. _`lenient`: cmlenz@820: cmlenz@820: Lenient Mode cmlenz@820: ------------ cmlenz@820: cmlenz@820: If you instruct Genshi to use the lenient variable lookup mode, it allows you cmlenz@820: to access variables that are not defined, without raising an ``UndefinedError``. cmlenz@820: cmlenz@820: This mode can be chosen by passing the ``lookup='lenient'`` keyword argument to cmlenz@820: the template initializer, or by passing the ``variable_lookup='lenient'`` cmlenz@820: keyword argument to the ``TemplateLoader`` initializer: cmlenz@820: cmlenz@820: .. code-block:: pycon cmlenz@820: cmlenz@820: >>> from genshi.template import MarkupTemplate cmlenz@820: >>> tmpl = MarkupTemplate('

${doh}

', lookup='lenient') cmlenz@902: >>> print(tmpl.generate().render('xhtml')) cmlenz@500:

cmlenz@500: cmlenz@500: You *will* however get an exception if you try to call an undefined variable, or cmlenz@820: do anything else with it, such as accessing its attributes: cmlenz@820: cmlenz@820: .. code-block:: pycon cmlenz@500: cmlenz@500: >>> from genshi.template import MarkupTemplate cmlenz@820: >>> tmpl = MarkupTemplate('

${doh.oops}

', lookup='lenient') cmlenz@902: >>> print(tmpl.generate().render('xhtml')) cmlenz@500: Traceback (most recent call last): cmlenz@500: ... cmlenz@500: UndefinedError: "doh" not defined cmlenz@500: cmlenz@500: If you need to know whether a variable is defined, you can check its type cmlenz@820: against the ``Undefined`` class, for example in a conditional directive: cmlenz@820: cmlenz@820: .. code-block:: pycon cmlenz@500: cmlenz@500: >>> from genshi.template import MarkupTemplate cmlenz@820: >>> tmpl = MarkupTemplate('

${type(doh) is not Undefined}

', cmlenz@820: ... lookup='lenient') cmlenz@902: >>> print(tmpl.generate().render('xhtml')) cmlenz@500:

False

cmlenz@500: cmlenz@500: Alternatively, the built-in functions defined_ or value_of_ can be used in this cmlenz@500: case. cmlenz@500: cmlenz@500: Custom Modes cmlenz@500: ------------ cmlenz@500: cmlenz@500: In addition to the built-in "lenient" and "strict" modes, it is also possible to cmlenz@500: use a custom error handling mode. For example, you could use lenient error cmlenz@500: handling in a production environment, while also logging a warning when an cmlenz@500: undefined variable is referenced. cmlenz@500: cmlenz@500: See the API documentation of the ``genshi.template.eval`` module for details. cmlenz@500: cmlenz@500: cmlenz@500: Built-in Functions & Types cmlenz@500: ========================== cmlenz@500: cmlenz@500: The following functions and types are available by default in template code, in cmlenz@500: addition to the standard built-ins that are available to all Python code. cmlenz@500: cmlenz@500: .. _`defined`: cmlenz@500: cmlenz@500: ``defined(name)`` cmlenz@500: ----------------- cmlenz@500: This function determines whether a variable of the specified name exists in cmlenz@500: the context data, and returns ``True`` if it does. cmlenz@500: cmlenz@500: .. _`value_of`: cmlenz@500: cmlenz@500: ``value_of(name, default=None)`` cmlenz@500: -------------------------------- cmlenz@500: This function returns the value of the variable with the specified name if cmlenz@500: such a variable is defined, and returns the value of the ``default`` cmlenz@500: parameter if no such variable is defined. cmlenz@500: cmlenz@500: .. _`Markup`: cmlenz@500: cmlenz@500: ``Markup(text)`` cmlenz@500: ---------------- cmlenz@500: The ``Markup`` type marks a given string as being safe for inclusion in markup, cmlenz@500: meaning it will *not* be escaped in the serialization stage. Use this with care, cmlenz@500: as not escaping a user-provided string may allow malicious users to open your cmlenz@500: web site to cross-site scripting attacks. cmlenz@500: cmlenz@500: .. _`Undefined`: cmlenz@500: cmlenz@500: ``Undefined`` cmlenz@500: ---------------- cmlenz@500: The ``Undefined`` type can be used to check whether a reference variable is cmlenz@500: defined, as explained in `error handling`_. cmlenz@500: cmlenz@500: cmlenz@500: .. _`directives`: cmlenz@500: cmlenz@500: ------------------- cmlenz@500: Template Directives cmlenz@500: ------------------- cmlenz@500: cmlenz@500: Directives provide control flow functionality for templates, such as conditions cmlenz@500: or iteration. As the syntax for directives depends on whether you're using cmlenz@500: markup or text templates, refer to the cmlenz@500: `XML Template Language `_ or cmlenz@500: `Text Template Language `_ pages for information.