Mercurial > genshi > mirror
diff doc/templates.txt @ 442:97544725bb7f trunk
Back out [510] and instead implement configurable error handling modes. The default is the old 0.3.x behaviour, but more strict error handling is available as an option.
author | cmlenz |
---|---|
date | Thu, 12 Apr 2007 22:40:49 +0000 |
parents | |
children | ec7890aa7c0b |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/doc/templates.txt @@ -0,0 +1,325 @@ +.. -*- mode: rst; encoding: utf-8 -*- + +======================== +Genshi Templating Basics +======================== + +Genshi provides a template engine that can be used for generating either +markup (such as HTML_ or XML_) or plain text. While both share some of the +syntax (and much of the underlying implementation) they are essentially +separate languages. + +.. _html: http://www.w3.org/html/ +.. _xml: http://www.w3.org/XML/ + +This document describes the common parts of the template engine and will be most +useful as reference to those developing Genshi templates. Templates are XML or +plain text files that include processing directives_ that affect how the +template is rendered, and template expressions_ that are dynamically substituted +by variable data. + + +.. contents:: Contents + :depth: 3 +.. sectnum:: + +-------- +Synopsis +-------- + +A Genshi *markup template* is a well-formed XML document with embedded Python +used for control flow and variable substitution. Markup templates should be +used to generate any kind of HTML or XML output, as they provide many advantages +over simple text-based templates (such as automatic escaping of strings). + +The following illustrates a very basic Genshi markup template:: + + <?python + title = "A Genshi Template" + fruits = ["apple", "orange", "kiwi"] + ?> + <html xmlns:py="http://genshi.edgewall.org/"> + <head> + <title py:content="title">This is replaced.</title> + </head> + + <body> + <p>These are some of my favorite fruits:</p> + <ul> + <li py:for="fruit in fruits"> + I like ${fruit}s + </li> + </ul> + </body> + </html> + +This example shows: + +(a) a Python code block, using a processing instruction +(b) the Genshi namespace declaration +(c) usage of templates directives (``py:content`` and ``py:for``) +(d) an inline Python expression (``${fruit}``). + +A *text template* is a simple plain text document that can also contain embedded +Python code. Text templates can be used to generate simple *non-markup* text +formats, such as the body of an plain text email. For example:: + + Dear $name, + + These are some of my favorite fruits: + #for fruit in fruits + * $fruit + #end + + +---------- +Python API +---------- + +The Python code required for templating with Genshi is generally based on the +following pattern: + +* Attain a ``MarkupTemplate`` or ``TextTemplate`` object from a string or + file-like object containing the template source. This can either be done + directly, or through a ``TemplateLoader`` instance. +* Call the ``generate()`` method of the template, passing any data that should + be made available to the template as keyword arguments. +* Serialize the resulting stream using its ``render()`` method. + +For example:: + + >>> from genshi.template import MarkupTemplate + >>> tmpl = MarkupTemplate('<h1>Hello, $name!</h1>') + >>> stream = tmpl.generate(name='world') + >>> print stream.render() + <h1>Hello, world!</h1> + +Using a text template is similar:: + + >>> from genshi.template import TextTemplate + >>> tmpl = TextTemplate('Hello, $name!') + >>> stream = tmpl.generate(name='world') + >>> print stream.render() + Hello, world! + +.. note:: See the Serialization_ section of the `Markup Streams`_ page for + information on configuring template output options. + +.. _serialization: streams.html#serialization +.. _`Markup Streams`: streams.html + +Using a template loader provides the advantage that “compiled” templates are +automatically cached, and only parsed again when the template file changes. In +addition, it enables the use of a *template search path*, allowing template +directories to be spread across different file-system locations. Using a +template loader would generally look as follows:: + + from genshi.template import TemplateLoader + loader = TemplateLoader([templates_dir1, templates_dir2]) + tmpl = loader.load('test.html') + stream = tmpl.generate(title='Hello, world!') + print stream.render() + +See the `API documentation <api/index.html>`_ for details on using Genshi via +the Python API. + + +.. _`expressions`: + +------------------------------------ +Template Expressions and Code Blocks +------------------------------------ + +Python_ expressions can be used in text and directive arguments. An expression +is substituted with the result of its evaluation against the template data. +Expressions in text (which includes the values of non-directive attributes) need +to prefixed with a dollar sign (``$``) and usually enclosed in curly braces +(``{…}``). + +.. _python: http://www.python.org/ + +If the expression starts with a letter and contains only letters, digits, dots, +and underscores, the curly braces may be omitted. In all other cases, the +braces are required so that the template processor knows where the expression +ends:: + + >>> from genshi.template import MarkupTemplate + >>> tmpl = MarkupTemplate('<em>${items[0].capitalize()} item</em>') + >>> print tmpl.generate(items=['first', 'second']) + <em>First item</em> + +Expressions support the full power of Python. In addition, it is possible to +access items in a dictionary using “dotted notation” (i.e. as if they were +attributes), and vice-versa (i.e. access attributes as if they were items in a +dictionary):: + + >>> from genshi.template import MarkupTemplate + >>> tmpl = MarkupTemplate('<em>${dict.foo}</em>') + >>> print tmpl.generate(dict={'foo': 'bar'}) + <em>bar</em> + +Because there are two ways to access either attributes or items, expressions +do not raise the standard ``AttributeError`` or ``IndexError`` exceptions, but +rather an exception of the type ``UndefinedError``. The same kind of error is +raised when you try to access a top-level variable that is not in the context +data. + + +.. _`code blocks`: + +Code Blocks +=========== + +XML templates also support full Python code blocks using the ``<?python ?>`` +processing instruction:: + + <div> + <?python + from genshi.builder import tag + def greeting(name): + return tag.b('Hello, %s!' % name') ?> + ${greeting('world')} + </div> + +This will produce the following output:: + + <div> + <b>Hello, world!</b> + </div> + +Code blocks can import modules, define classes and functions, and basically do +anything you can do in normal Python code. What code blocks can *not* do is to +produce content that is included directly in the generated page. + +.. note:: Using the ``print`` statement will print to the standard output + stream, just as it does for other Python code in your application. + +This feature is not supposed to encourage mixing application code into +templates, which is generally considered bad design. If you're using many code +blocks, that me be a sign that you should move such code into separate Python +modules. + +.. note:: Code blocks are not currently supported in text templates. + + +.. _`error handling`: + +Error Handling +============== + +By default, Genshi allows you to access variables that are not defined, without +raising a ``NameError`` exception as regular Python code would:: + + >>> from genshi.template import MarkupTemplate + >>> tmpl = MarkupTemplate('<p>${doh}</p>') + >>> print tmpl.generate().render('xhtml') + <p></p> + +You *will* however get an exception if you try to call an undefined variable, or +do anything else with it, such as accessing its attributes:: + + >>> from genshi.template import MarkupTemplate + >>> tmpl = MarkupTemplate('<p>${doh.oops}</p>') + >>> print tmpl.generate().render('xhtml') + Traceback (most recent call last): + ... + UndefinedError: "doh" not defined + +If you need to know whether a variable is defined, you can check its type +against the ``Undefined`` class, for example in a conditional directive:: + + >>> from genshi.template import MarkupTemplate + >>> tmpl = MarkupTemplate('<p>${type(doh) is not Undefined}</p>') + >>> print tmpl.generate().render('xhtml') + <p>False</p> + +Alternatively, the built-in functions defined_ or value_of_ can be used in this +case. + +Strict Mode +----------- + +In addition to the default "lenient" error handling, Genshi lets you use a less +forgiving mode if you prefer errors blowing up loudly instead of being ignored +silently. + +This mode can be chosen by passing the ``lookup='strict'`` keyword argument to +the template initializer, or by passing the ``variable_lookup='strict'`` keyword +argument to the ``TemplateLoader`` initializer:: + + >>> from genshi.template import MarkupTemplate + >>> tmpl = MarkupTemplate('<p>${doh}</p>', lookup='strict') + >>> print tmpl.generate().render('xhtml') + Traceback (most recent call last): + ... + UndefinedError: "doh" not defined + +When using strict mode, any reference to an undefined variable, as well as +trying to access an non-existing item or attribute of an object, will cause an +``UndefinedError`` to be raised immediately. + +.. note:: While this mode is currently not the default, it may be promoted to + the default in future versions of Genshi. In general, the default + lenient error handling mode can be considered dangerous as it silently + ignores typos. + +Custom Modes +------------ + +In addition to the built-in "lenient" and "strict" modes, it is also possible to +use a custom error handling mode. For example, you could use lenient error +handling in a production environment, while also logging a warning when an +undefined variable is referenced. + +See the API documentation of the ``genshi.template.eval`` module for details. + + +Built-in Functions & Types +========================== + +The following functions and types are available by default in template code, in +addition to the standard built-ins that are available to all Python code. + +.. _`defined`: + +``defined(name)`` +----------------- +This function determines whether a variable of the specified name exists in +the context data, and returns ``True`` if it does. + +.. _`value_of`: + +``value_of(name, default=None)`` +-------------------------------- +This function returns the value of the variable with the specified name if +such a variable is defined, and returns the value of the ``default`` +parameter if no such variable is defined. + +.. _`Markup`: + +``Markup(text)`` +---------------- +The ``Markup`` type marks a given string as being safe for inclusion in markup, +meaning it will *not* be escaped in the serialization stage. Use this with care, +as not escaping a user-provided string may allow malicious users to open your +web site to cross-site scripting attacks. + +.. _`Undefined`: + +``Undefined`` +---------------- +The ``Undefined`` type can be used to check whether a reference variable is +defined, as explained in `error handling`_. + + +.. _`directives`: + +------------------- +Template Directives +------------------- + +Directives provide control flow functionality for templates, such as conditions +or iteration. As the syntax for directives depends on whether you're using +markup or text templates, refer to the +`XML Template Language <xml-templates.html>`_ or +`Text Template Language <text-templates.html>`_ pages for information.