Tempita templating language¶
This document describes the Tempita Templating Engine.It was originally developed by Ian Bicking.The original version of Tempita is available onPyPI and documentation can be found athttps://github.com/TurboGears/tempita/blob/main/README.rst.
Tempita is used internally in Cython for non-trivial code-generation purposes.It is also available to users as a public API for generating or customizing code when building a Cython module.
Overview¶
Tempita is a simple templating language, similar to Jinja.Basic usage is as follows:
fromCython.TempitaimportTemplatetmpl1=Template(template_string)# load from stringtmpl2=Template.from_filename(path)# load template from filetmpl1.substitute(**kwargs)# pass parameters as named arguments.tmpl2.sub_vars(mapping)# pass an argument as dict-like mapping.
Tempita also provides a shortcut function:
fromCython.Tempitaimportsubsub(template_string,**kwargs)
Templating Syntax¶
Below are the different directive types and expression forms supported.
Simple Expression Substitution¶
Write expressions as{{...}}
, which are evaluated in the template context.
Example:
>>>sub("Hello {{name}}!",name="World")'Hello World!'
Expressions can be any valid Python expression: arithmetic, attribute access,indexing, function calls etc.:
>>>sub("2 * 3 = {{2 * 3}}")'2 * 3 = 6'>>>classUser:...name='Mark'...>>>sub("User: {{ user.name }}",user=User())'User: Mark'>>>sub("Hex: {{ hex(num) }}",num=10)'Hex: 0xa'>>>sub("Number: {{ func(num) }}",func=abs,num=-10)'Number: 10'>>>sub("List[0] = {{ mylist[0] }}",mylist=[10,20,30])'List[0] = 10'>>>sub("Dist['name'] = {{ mydict['name'] }}",mydict={'name':'Miss Islington'})"Dist['name'] = Miss Islington">>>sub("Uppercase {{ string.upper() }}",string="hello")'Uppercase HELLO'
Default Values¶
You can specify a default value for a variable using thedefault
keyword,and overwrite it by passing it as argument to the template substitution:
>>>tmpl=Template("""...{{default name = "Sir Lancelot the Brave"}}...My name is {{name}}...."""...)>>>tmpl.substitute()'My name is Sir Lancelot the Brave.\n'>>>tmpl.substitute(name="Sir Bedevere the Wise")'My name is Sir Bedevere the Wise.\n'
Inline Python Code¶
Arbitrary Python statements can be inserted inline with{{py:...}}
.These do not emit output directly but can modify the template context.
>>>tmpl=Template("""...{{py: x = 5}}...Value of x: {{x}}...""")>>>tmpl.substitute()'Value of x: 5\n'
Comments¶
Comments are introduced with{{#...}}
. They are completely removedfrom output.
>>>sub("Hello {{# This is a comment }}World.")'Hello World.'
Custom delimiter characters¶
In cases where the double braces that Tempita normally uses as delimiters ({{...}}
) get in the way,you can replace them with other characters:
>>>sub("Show literal braces: {{<<name>>}}",delimiters=['<<','>>'],name="x")'Show literal braces: {{x}}'
Note
Thedelimiters
can also be passed as an argument to theTemplate
class,
thus keeping them together with the template definition that uses them,rather than needing to remember them later in the substitution step.
Template("...",delimiters=['((','))'])
Filters¶
You can post-process the result of an expression before it is written into the template.Below, thefiltername
refers to a callable (function) that will be called by the template engineand receives the result of the preceding expression as argument.Whatever it outputs will then be written into the templating result.
{{expression|filtername}}
Example:
>>>sub("Lowercase: {{ name | lower }}",name="ALICE",lower=lambdax:x.lower())'Lowercase: alice'
Control Blocks¶
Tempita supports Python-like control flow directives.These are evaluated at runtime and allow conditional rendering and iteration.
Each block has an explicitclosing directive such asendif
orendfor
.
if
/elif
/else
block¶
Conditional execution based on an expression.
Syntax:
{{ifcondition}}...contentiftrue...{{elifother_condition}}...contentifelifistrue...{{else}}...contentifallabovearefalse...{{endif}}
Example:
>>>tmpl=Template("""...{{if x > 0}}...Positive...{{elif x == 0}}...Zero...{{else}}...Negative...{{endif}}...""")>>>tmpl.substitute(x=-5)'Negative\n'
for
block¶
Iterate over a sequence and render the body for each element.
Syntax:
{{forvariniterable}}...bodyusing{{var}}...{{endfor}}
You may also unpack tuples or iterate with multiple variables, as known from Python for-loops.
Example:
>>>tmpl=Template("""...{{for name, score in scores}}...{{name}}: {{score}}...{{endfor}}...""")>>>print(tmpl.substitute(scores=[("Alice",95),("Bob",88)]))Alice: 95Bob: 88
Note
As known from Python’s for-loop, Tempitafor
blocks also support{{break}}
and{{continue}}
statements.
Nesting Blocks¶
Blocks can be nested arbitrarily.
>>>tmpl=Template("""...{{for item in items}}... {{if item < 0}}... {{continue}}... {{elif item % 2 == 0}}... {{item}} is even... {{else}}... {{item}} is odd... {{endif}}...{{endfor}}...""")>>>print(tmpl.substitute(items=[-1,1,2,3])) 1 is odd 2 is even 3 is odd
Indentation, Whitespace, and Newlines¶
Templates preserve whitespace exactly as written around directives.
Newlines in templates become newlines in outputs.
Indentation is preserved, so control block bodies should be indentedmeaningfully by the template author if you want nice output.
Examples¶
Here are consolidated examples showing most of the available syntax in use:
fromCython.TempitaimportTemplatetmpl=Template("""Header{{# This is a comment }}{{if user["is_admin"]}} Welcome, Admin {{user["name"]}}!{{else}} Hello, {{user["name"] or 'Guest'}}.{{endif}}{{for item in items}} * {{item["name"]}}: {{item["value"]}}{{endfor}}{{py: x = 1 + 2}}Inline code result: {{x}}Expression: 1 + 2 = {{1 + 2}}""")print(tmpl.substitute(user={'name':'Bob','is_admin':False},items=[{'name':'A','value':10},{'name':'B','value':20}]))
Output:
HeaderHello,Bob.*A:10*B:20Expression:1+2=3
Notes and Limitations¶
Templates are evaluated when they are substituted, rather than when they are created; errors show up when calling
substitute()
.There is no sandbox: template code can execute arbitrary Python expressions.