Welcome to Tonnikala’s documentation!
Tonnikala is the latest reincarnation among the Python templating languages that feed on Kid-inspired XML syntax. It doesn’t use the tagstreams and trees of Genshi or Kid, but follows in footsteps of Chameleon and Kajiki in making the template to compile into Python bytecode directly. The syntax is very close to that of Kajiki, but the internals are very different: Tonnikala writes code as Abstract Syntax Trees and optimizes the resulting trees extensively. In addition, there is an optional speed-up module, that provides a specialized class used for output buffering.
Examples
from tonnikala.loader import Loader
template_source = u"""
<table>
<tr py:for="row in table">
<py:for each="key, value in row.items()"
><td>$key</td><td>$literal(value)</td></py:for>
</tr>
</table>
"""
template = Loader().load_string(template_source)
ctx = {
'table': [dict(a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10)
for x in range(1000)]
}
print(template.render(ctx))
Variable interpolation
Within attributes and text, all contents starting with $ followed
by a {, an alphabetic character or _ is considered an interpolated expression.
If the interpolated expression starts with ${, the expression continues until the matching } token.
Otherwise the interpolation consists of an identifier, followed by any number of attribute accesses,
indexing brackets [...], and method call operators (...), without any
intervening whitespace (except within the brackets). The expression
parsing stops whenever the next token cannot match this rule anymore.
While the form
HELLO, ${user.name.upper()}.
is accepted, it is also perfectly OK to write
HELLO, $user.name.upper().
In the above code, user is an object with name attribute or property, which
evaluates to a string; upper() method is called on the resulting string.
Suppose the user’s name is Antti Haapala, the resulting output would be
HELLO, ANTTI HAAPALA..
The rules also ensure that you can do an interpolation as follows:
Your word $digit has the integer value ${{'one': 1, 'two': 2}[digit]}
Now, if digit == 'one', the output of this fragment would be
Your word one has the integer value 1.
An interpolated expression is auto-escaped appropriately for its context. If you do
not want to be the expression to be escaped you can bracket it with a function
call to literal(), or in markupsafe.Markup. The literal is especially
efficient as it is optimized away in the compile time whenever possible.
Template inheritance
base.tk
<html>
<title><py:block name="title_block">I am $title</py:block></title>
<py:def function="foo()">I can be overridden too!</py:def>
<h1>${title_block()}</h1>
${foo()}
</html>
child.tk
<py:extends href="base.tk">
<py:block name="title_block">But I am $title instead</py:block>
<py:def function="foo()">I have overridden the function in parent template</py:def>
</py:extends>
Template imports
importable.tk
<html>
<py:def function="foo()">I am an importable function</py:def>
</html>
importer.tk
<html>
<py:import href="importable.tk" alias="imp" />
${imp.foo()}
</html>
FileLoader
To load templates from files, use the tonnikala.FileLoader class:
loader = FileLoader(paths=['/path/to/templates'])
template = loader.load('child.tk')
A FileLoader currently implicitly caches all loaded templates in memory.
Template
To render the template:
result = template.render(ctx)
You can specify a block, or no-argument def to render explicitly:
result = template.render(ctx, funcname='title_block')
Pyramid integration
Include ‘tonnikala.pyramid’ into your config to enable Tonnikala. When included, Tonnikala adds the following configuration directives:
add_tonnikala_extensions(*extensions)Registers Tonnikala renderer for these template extensions. By default Tonnikala is not registered as a renderer for any extension. For example:
config.add_tonnikala_extensions('.html', '.tk')would enable Tonnikala renderer for templates with either of these extensions.add_tonnikala_search_paths(*paths)Adds the given paths to the end of Tonnikala search paths that are searched for templates. These can be absolute paths, or
package.module:directory/subdirectory-style asset specs. By default no search path is set (though of course you can use an asset spec for template).set_tonnikala_reload(reload)If
True, makes Tonnikala not cache templates. Default isFalse.set_tonnikala_l10n(reload)If
True, makes Tonnikala translate templates. Default isFalse.
These 4 can also be controlled by tonnikala.extensions, tonnikala.search_paths, tonnikala.reload and tonnikala.l10n respectively in the deployment settings (the .ini files).
If tonnikala.reload is not set, Tonnikala shall follow the pyramid.reload_templates setting.
set_debug_templates(debug)If
True, makes Tonnikala skip some optimizations that make debugging harder.
Status
Stable. Features:
Structural elements
py:if,py:unless,py:def,py:for,py:replace,py:contentBasic template inheritance:
py:extendsandpy:block; the child template also inherits top level function declarations from the parent template, and the child can override global functions that the parent defines and uses.Expression interpolation using
$simple_identifierand${complex + python + "expression"}Boolean attributes:
<tag attr="${False}">,<tag attr="$True">Implicit escaping
Disabling implicit escaping (
literal())C speedups for Python 3
Importing def blocks from another template:
py:importBasic I18N using gettext.
Pyramid integration
Javascript as the target language (using
js:prefix)Overriding attributes, setting attrs from dictionary:
py:attrsUnderstandable exceptions and readable tracebacks on CPython
Lexical variable assignments with
py:with
Upcoming features:
Structural elements:
py:switch,py:case;py:elseforfor,ifandswitch.Custom tags mapping to
py:defI18N with optional in-parse-tree localization (partially done)
Pluggable frontend syntax engines (partially done)
METAL-like macros
Pluggable expression languages akin to Chameleon
Even better template inheritance
Better documentation