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@442: The following illustrates a very basic Genshi markup template:: 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: 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@445: The template would generate output similar to this:: 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: 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@442: formats, such as the body of an plain text email. For example:: 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@442: For example:: cmlenz@442: cmlenz@442: >>> from genshi.template import MarkupTemplate cmlenz@442: >>> tmpl = MarkupTemplate('

Hello, $name!

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

Hello, world!

cmlenz@442: cmlenz@442: Using a text template is similar:: cmlenz@442: cmlenz@442: >>> from genshi.template import TextTemplate cmlenz@442: >>> tmpl = TextTemplate('Hello, $name!') cmlenz@442: >>> stream = tmpl.generate(name='world') cmlenz@442: >>> print stream.render() cmlenz@442: Hello, world! cmlenz@442: cmlenz@442: .. note:: See the Serialization_ section of the `Markup Streams`_ page for cmlenz@442: information on configuring template output options. cmlenz@442: cmlenz@442: .. _serialization: streams.html#serialization 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@442: template loader would generally look as follows:: 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@442: ends:: 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@442: dictionary):: 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@478: raised when you try to use a top-level variable that is not in the context data. cmlenz@478: 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@442: XML templates also support full Python code blocks using the ```` cmlenz@442: processing instruction:: cmlenz@442: cmlenz@442:
cmlenz@442: cmlenz@442: ${greeting('world')} cmlenz@442:
cmlenz@442: cmlenz@442: This will produce the following output:: cmlenz@442: cmlenz@442:
cmlenz@442: Hello, world! cmlenz@442:
cmlenz@442: 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@442: produce content that is included directly in the generated page. 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@478: Unlike expressions, Python code in ```` processing instructions can cmlenz@478: not use item and attribute access in an interchangeable manner. That means that cmlenz@478: “dotted notation” is always attribute access, and vice-versa. cmlenz@478: cmlenz@478: The support for Python code blocks in templates is not supposed to encourage cmlenz@478: mixing application code into templates, which is generally considered bad cmlenz@478: design. If you're using many code blocks, that may be a sign that you should cmlenz@478: move such code into separate Python modules. cmlenz@442: cmlenz@442: .. note:: Code blocks are not currently supported in text templates. cmlenz@442: cmlenz@442: cmlenz@442: .. _`error handling`: cmlenz@442: cmlenz@442: Error Handling cmlenz@442: ============== cmlenz@442: cmlenz@442: By default, Genshi allows you to access variables that are not defined, without cmlenz@442: raising a ``NameError`` exception as regular Python code would:: cmlenz@442: cmlenz@442: >>> from genshi.template import MarkupTemplate cmlenz@442: >>> tmpl = MarkupTemplate('

${doh}

') 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@442: do anything else with it, such as accessing its attributes:: cmlenz@442: cmlenz@442: >>> from genshi.template import MarkupTemplate cmlenz@442: >>> tmpl = MarkupTemplate('

${doh.oops}

') 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@442: against the ``Undefined`` class, for example in a conditional directive:: cmlenz@442: cmlenz@442: >>> from genshi.template import MarkupTemplate cmlenz@442: >>> tmpl = MarkupTemplate('

${type(doh) is not Undefined}

') 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: Strict Mode cmlenz@442: ----------- cmlenz@442: cmlenz@442: In addition to the default "lenient" error handling, Genshi lets you use a less cmlenz@442: forgiving mode if you prefer errors blowing up loudly instead of being ignored cmlenz@442: silently. cmlenz@442: cmlenz@442: This mode can be chosen by passing the ``lookup='strict'`` keyword argument to cmlenz@442: the template initializer, or by passing the ``variable_lookup='strict'`` keyword cmlenz@442: argument to the ``TemplateLoader`` initializer:: cmlenz@442: cmlenz@442: >>> from genshi.template import MarkupTemplate cmlenz@442: >>> tmpl = MarkupTemplate('

${doh}

', lookup='strict') 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: When using strict mode, any reference to an undefined variable, as well as cmlenz@442: trying to access an non-existing item or attribute of an object, will cause an cmlenz@442: ``UndefinedError`` to be raised immediately. cmlenz@442: cmlenz@442: .. note:: While this mode is currently not the default, it may be promoted to cmlenz@442: the default in future versions of Genshi. In general, the default cmlenz@442: lenient error handling mode can be considered dangerous as it silently cmlenz@442: ignores typos. 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.