Kajiki Template Language

Kajiki provides a XML-based template language that is heavily inspired by Kid, and Genshi which in turn was inspired by a number of existing template languages, namely XSLT, TAL, and PHP.

This document describes the template language and will be most useful as reference to those developing Kajiki XML 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.

Template Directives

Directives are elements and/or attributes in the template that are identified by the namespace py:. They can affect how the template is rendered in a number of ways: Kajiki provides directives for conditionals and looping, among others.

All directives can be applied as attributes, and some can also be used as elements. The if directives for conditionals, for example, can be used in both ways:

<html>
  ...
  <div py:if="foo">
    <p>Bar</p>
  </div>
  ...
</html>

This is basically equivalent to the following:

<html>
  ...
  <py:if test="foo">
    <div>
      <p>Bar</p>
    </div>
  </py:if>
  ...
</html>

The rationale behind the second form is that directives do not always map naturally to elements in the template. In such cases, the py:strip directive can be used to strip off the unwanted element, or the directive can simply be used as an element.

Conditional Sections

py:if

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

<div>
  <b py:if="foo">${bar}</b>
</div>

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

<div>
  <b>Hello</b>
</div>

But setting foo=False would result in the following output:

<div>
</div>

This directive can also be used as an element:

<div>
  <py:if test="foo">
    <b>${bar}</b>
  </py:if>
</div>

py:switch

The py:switch directive, in combination with the directives py:case and py:else provides advanced conditional processing for rendering one of several alternatives. The first matching py:case branch is rendered, or, if no py:case branch matches, the py:else branch is rendered.

The nested py:case directives will be tested for equality to the parent py:switch value:

<div>
  <py:switch test="1">
    <span py:case="0">0</span>
    <span py:case="1">1</span>
    <span py:else="">2</span>
  </py:switch>
</div>

This would produce the following output:

<div>
  <span>1</span>
</div>

Note

The py:switch directive can only be used as a standalone tag and cannot be applied as an attribute of a tag.

Looping

py:for

The element is repeated for every item in an iterable:

<ul>
  <li py:for="item in items">${item}</li>
</ul>

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

<ul>
  <li>1</li><li>2</li><li>3</li>
</ul>

This directive can also be used as an element:

<ul>
  <py:for each="item in items">
    <li>${item}</li>
  </py:for>
</ul>

Snippet Reuse

py:def

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

<div>
  <p py:def="greeting(name)" class="greeting">
    Hello, ${name}!
  </p>
  ${greeting('world')}
  ${greeting('everyone else')}
</div>

The above would be rendered to:

<div>
  <p class="greeting">
    Hello, world!
  </p>
  <p class="greeting">
    Hello, everyone else!
  </p>
</div>

If a macro doesn’t require parameters, it can be defined without the parenthesis. For example:

<div>
  <p py:def="greeting" class="greeting">
    Hello, world!
  </p>
  ${greeting()}
</div>

The above would be rendered to:

<div>
  <p class="greeting">
    Hello, world!
  </p>
</div>

This directive can also be used as an element:

<div>
  <py:def function="greeting(name)">
    <p class="greeting">Hello, ${name}!</p>
  </py:def>
</div>

Variable Binding

py:with

The py: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:

<div>
  <span py:with="y=7; z=x+10">$x $y $z</span>
</div>

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

<div>
  <span>42 7 52</span>
</div>

This directive can also be used as an element:

<div>
  <py:with vars="y=7; z=x+10">$x $y $z</py:with>
</div>

Structure Manipulation

py:attrs

This directive adds, modifies or removes attributes from the element:

<ul>
  <li py:attrs="foo">Bar</li>
</ul>

Given foo={'class': 'collapse'} in the template context, this would produce:

<ul>
  <li class="collapse">Bar</li>
</ul>

Attributes with the value None are omitted, so given foo={'class': None} in the context for the same template this would produce:

<ul>
  <li>Bar</li>
</ul>

Note

This directive can only be used as an attribute.

py:content

This directive replaces any nested content with the result of evaluating the expression:

<ul>
  <li py:content="bar">Hello</li>
</ul>

Given bar='Bye' in the context data, this would produce:

<ul>
  <li>Bye</li>
</ul>

This directive can only be used as an attribute.

py:replace

This directive replaces the element itself with the result of evaluating the expression:

<div>
  <span py:replace="bar">Hello</span>
</div>

Given bar='Bye' in the context data, this would produce:

<div>
  Bye
</div>

This directive can also be used as an element (since version 0.5):

<div>
  <py:replace value="title">Placeholder</py:replace>
</div>

py:strip

This directive conditionally strips the top-level element from the output. When the value of the py:strip attribute evaluates to True, the element is stripped from the output:

<div>
  <div py:strip="True"><b>foo</b></div>
</div>

This would be rendered as:

<div>
  <b>foo</b>
</div>

As a shorthand, if the value of the py:strip attribute is empty, that has the same effect as using a truth value (i.e. the element is stripped).

Includes

To reuse common snippets of template code, you can include other files using py:include and py:import.

py:include

Includes the text of another template verbatim. The precise semantics of this tag depend on the TemplateLoader being used, as the TemplateLoader is used to parse the name of the template being included and render its contents into the current template. For instance, with the FileLoader, you might use the following:

<py:include href="path/to/base.txt"/>

whereas in the PackageLoader you would use

<py:include href="package1.package2.base"/>

py:import

With py:import, you can make the functions defined in another template available without expanding the full template in-place. Suppose that we saved the following template in a file lib.xml:

<py:def function="evenness(n)">
   <py:if test="n%2==0">even</py:if><py:else>odd</py:else>
</py:def>

Then (using the FileLoader) we could write a template using the evenness function as follows:

<div>
   <py:import hef="lib.xml" alias="lib"/>
   <ul>
      <li py:for="i in range(sz)">$i is ${lib.evenness(i)}</li>
   </ul>
</div>

Converting Genshi Templates to Kajiki

Kajiki is a fast template engine which is 90% compatible with Genshi, all of Genshi directives work in Kajiki too apart those involved in templates inheritance as Kajiki uses blocks instead of XInclude and XPath.

Simple templates hierarchies (like the one coming from TurboGears quickstart) can be moved to Kajiki blocks in a matter of seconds through the gearbox patch command.

Note

Please note that this guide only works on version 2.3.6 and greater.

Note

It’s suggested to try this steps on a newly quickstarted Genshi application and then test them on your real apps when you are confident with the whole process.

Enabling Kajiki Templates

Enabling Kajiki support involves changing the base_config.default_renderer option in your app_cfg.py and adding kajiki to the renderers:

# Add kajiki support
base_config.renderers.append('kajiki')

# Set the default renderer
base_config.default_renderer = 'kajiki'

Adapting the Master Template

The only template we will need to adapt by hand is our master.html template, everything else will be done automatically. So the effort of porting an application from Genshi to Kajiki is the same independently from the size of the application.

First of all we will need to remove the py:strip and xmlns attributes from the html tag:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:py="http://genshi.edgewall.org/"
      xmlns:xi="http://www.w3.org/2001/XInclude"
      py:strip="">

should became:

<html>

Then let’s adapt our head tag to make it so that the content from templates that extend our master gets included inside it:

<head py:match="head" py:attrs="select('@*')">
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <meta charset="${response.charset}" />
  <title py:if="False">Your generic title goes here</title>
  <meta py:replace="select('*')"/>
  <link rel="stylesheet" type="text/css" media="screen" href="${tg.url('/css/bootstrap.min.css')}" />
  <link rel="stylesheet" type="text/css" media="screen" href="${tg.url('/css/style.css')}" />
</head>

should became:

<head>
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <meta charset="${response.charset}" />
  <title py:if="False">Your generic title goes here</title>
  <py:block name="head"/>
  <link rel="stylesheet" type="text/css" media="screen" href="${tg.url('/css/bootstrap.min.css')}" />
  <link rel="stylesheet" type="text/css" media="screen" href="${tg.url('/css/style.css')}" />
</head>

Then we do the same with the body tag by disabling it as a block and placing a block with the same name inside of it:

<body py:match="body" py:attrs="select('@*')">
  <!-- Navbar -->
  [...]

  <div class="container">
    <!-- Flash messages -->
    <py:with vars="flash=tg.flash_obj.render('flash', use_js=False)">
      <div class="row">
        <div class="col-md-8 col-md-offset-2">
          <div py:if="flash" py:replace="Markup(flash)" />
        </div>
      </div>
    </py:with>

    <!-- Main included content -->
    <div py:replace="select('*|text()')"/>
  </div>
</body>

Which should became:

<body>
  <!-- Navbar -->
  [...]

  <div class="container">
    <!-- Flash messages -->
    <py:with vars="flash=tg.flash_obj.render('flash', use_js=False)">
      <div class="row">
        <div class="col-md-8 col-md-offset-2">
          <div py:if="flash" py:replace="Markup(flash)" />
        </div>
      </div>
    </py:with>

    <!-- Main included content -->
    <py:block name="body"/>
  </div>
</body>

What is did is replacing all the XPath expressions that lead to insert content from the child templates into head and body with two head and body blocks. So our child templates will be able to rely on those blocks to inject their content into the master.

Last importat step is renaming the master template, as Kajiki in turbogears uses .xhtml extension we will need to rename master.html to master.xhtml:

$ find ./ -iname 'master.html' -exec sh -c 'mv {} `dirname {}`/master.xhtml' \;

Note

The previous expression will rename the master file if run from within your project directory.

Upgrading Child Templates

There are three things we need to do to upgrade all our child templates to Kajiki:

  • Replace xi:include with py:extends
  • Strip <html> tags to avoid a duplicated root tag
  • Replace <head> tag with a kajiki block
  • Replace <body> tag with a kajiki block

To perform those changes we can rely on a simple but helpful gearbox command to patch all our templates by replacing xi:include with py:extends which is used and recognized by Kajiki.

Just move inside the root of your project and run:

$ gearbox patch -R '*.html' 'xi:include href="master.html"' -r 'py:extends href="master.xhtml"'

You should get an output similar to:

6 files matching
! Patching /private/tmp/prova/prova/templates/about.html
! Patching /private/tmp/prova/prova/templates/data.html
! Patching /private/tmp/prova/prova/templates/environ.html
! Patching /private/tmp/prova/prova/templates/error.html
! Patching /private/tmp/prova/prova/templates/index.html
! Patching /private/tmp/prova/prova/templates/login.html

Which means that all our templates apart from master.html got patched properly and now correctly use py:extends.

Now we can start adapting our tags to move them to kajiki blocks.

First of all we will need to strip the html from all the templates apart master.xhtml to avoid ending up with duplicated root tag:

$ gearbox patch -R '*.html' 'xmlns="http://www.w3.org/1999/xhtml"' -r 'py:strip=""'

Then we will need to do the same for the head tag:

$ gearbox patch -R '*.html' '<head>' -r '<head py:block="head" py:strip="True">'

Then repeat for body:

$ gearbox patch -R '*.html' '<body>' -r '<body py:block="body" py:strip="True">'

Now that all our templates got upgraded from Genshi to Kajiki, we must remember to rename them all, like we did for master:

$ find ./ -iname '*.html' -exec sh -c 'mv {} `dirname {}`/`basename {} .html`.xhtml' \;

Restarting your application now should lead to a properly working page equal to the original Genshi one.

Congratulations, you successfully moved your templates from Genshi to Kajiki.