Edgewall Software

Genshi Text Template Language

In addition to the XML-based template language, Genshi provides a simple text-based template language, intended for basic plain text generation needs. The language is similar to Cheetah or Velocity.

This document describes the template language and will be most useful as reference to those developing Genshi text templates. Templates are XML files of some kind (such as XHTML) that include processing directives (elements or attributes identified by a separate namespace) that affect how the template is rendered, and template expressions that are dynamically substituted by variable data.

1   Python API

The Python code required for templating with Genshi is generally based on the following pattern:

  • Attain a TextTemplate object from a string or file 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 TextTemplate

tmpl = TextTemplate('$title')
stream = tmpl.generate(title='Hello, world!')
print stream.render('text')

That code would produce the following output:

Hello, world!

Using a template loader provides the advantage that “compiled” templates are automatically cached, and only parsed again when the template file changes:

from genshi.template import TemplateLoader

loader = TemplateLoader([templates_dir])
tmpl = loader.load('test.txt' cls=TextTemplate)
stream = tmpl.generate(title='Hello, world!')
print stream.render('text')

2   Template Expressions

Python expressions can be used in text and as arguments to directives. An expression is substituted with the result of its evaluation against the template data. Expressions need to prefixed with a dollar sign ($) and usually enclosed in curly braces ({…}).

If the expression starts with a letter and contains only letters and digits, the curly braces may be omitted. In all other cases, the braces are required so that the template processors knows where the expression ends:

>>> from genshi.template import TextTemplate
>>> tmpl = TextTemplate('${items[0].capitalize()} item')
>>> print tmpl.generate(items=['first', 'second'])
First item

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 TextTemplate
>>> tmpl = TextTemplate('${dict.foo}')
>>> print tmpl.generate(dict={'foo': 'bar'})
bar

Another difference is that you can access variables that are not defined, and won't get a NameError exception:

>>> from genshi.template import TextTemplate
>>> tmpl = TextTemplate('${doh}')
>>> print tmpl.generate()
<BLANKLINE>

You will however get a NameError if you try to call an undefined variable, or do anything else with it, such as accessing its attributes. If you need to know whether a variable is defined, you can check its type against the Undefined class, for example in an #if directive:

>>> from genshi.template import TextTemplate
>>> tmpl = TextTemplate('${type(doh) is Undefined}')
>>> print tmpl.generate()
True

3   Template Directives

Directives are lines starting with a # character followed immediately by the directive name. They can affect how the template is rendered in a number of ways: Genshi provides directives for conditionals and looping, among others.

Directives must be on separate lines, and the # character must be be the first non-whitespace character on that line. Each directive must be “closed” using a #end marker. You can add after the #end marker, for example to document which directive is being closed, or even the expression associated with that directive. Any text after #end (but on the same line) is ignored, and effectively treated as a comment.

If you want to include a literal # in the output, you need to escape it by prepending a backslash character (\). Note that this is not required if the # isn't immediately followed by a letter, or it isn't the first non-whitespace character on the line.

3.1   Conditional Sections

3.1.1   #if

The content is only rendered if the expression evaluates to a truth value:

#if foo
  ${bar}
#end

Given the data foo=True and bar='Hello' in the template context, this would produce:

Hello

3.1.2   #choose

The #choose directive, in combination with the directives #when and #otherwise provides advanced contional processing for rendering one of several alternatives. The first matching #when branch is rendered, or, if no #when branch matches, the #otherwise branch is be rendered.

If the #choose directive has no argument the nested #when directives will be tested for truth:

The answer is:
#choose
  #when 0 == 1
    0
  #end
  #when 1 == 1
    1
  #end
  #otherwise
    2
  #end
#end

This would produce the following output:

The answer is:
    1

If the #choose does have an argument, the nested #when directives will be tested for equality to the parent #choose value:

The answer is:
#choose 1
  #when 0
    0
  #end
  #when 1
    1
  #end
  #otherwise
    2
  #end
#end

This would produce the following output:

The answer is:
    1

3.2   Looping

3.2.1   #for

The content is repeated for every item in an iterable:

Your items:
#for item in items
  * ${item}
#end

Given items=[1, 2, 3] in the context data, this would produce:

Your items
  * 1
  * 2
  * 3

3.3   Snippet Reuse

3.3.1   #def

The #def directive can be used to create macros, i.e. snippets of template text that have a name and optionally some parameters, and that can be inserted in other places:

#def greeting(name)
  Hello, ${name}!
#end
${greeting('world')}
${greeting('everyone else')}

The above would be rendered to:

Hello, world!
Hello, everyone else!

If a macro doesn't require parameters, it can be defined as well as called without the parenthesis. For example:

#def greeting
  Hello, world!
#end
${greeting}

The above would be rendered to:

Hello, world!

3.4   Variable Binding

3.4.1   #with

The #with directive lets you assign expressions to variables, which can be used to make expressions inside the directive less verbose and more efficient. For example, if you need use the expression author.posts more than once, and that actually results in a database query, assigning the results to a variable using this directive would probably help.

For example:

Magic numbers!
#with y=7; z=x+10
  $x $y $z
#end

Given x=42 in the context data, this would produce:

Magic numbers!
  42 7 52

Note that if a variable of the same name already existed outside of the scope of the #with directive, it will not be overwritten. Instead, it will have the same value it had prior to the #with assignment. Effectively, this means that variables are immutable in Genshi.

4   Comments

Lines where the first non-whitespace characters are ## are removed from the output, and can thus be used for comments. This can be escaped using a backslash.


See also: genshi.template, Genshi XML Template Language, Documentation, GenshiRecipes

Last modified 8 years ago Last modified on Dec 14, 2015, 6:22:32 AM