Stpl2 is a compatible reimplementation of bottle.py's SimpleTemplate Engine.
Stpl2 is under active development but can be considered production-ready.
Stable releases are available on PyPI.
pip install stpl2
Or, if you're brave enough for using the in-development code, you can download straight from github.
pip install https://github.com/ergoithz/stpl2.git
This project aims full compatibility with bottle.py SimpleTemplate Engine template syntax, and started as a replacement for streaming templates using yield.
Stpl2 is very simple, templates are parsed line by line, yielding readable high quality python code you can find on Template.pycode instance attribute, and then compiled, cached and wrapped on-demand into TemplateContext which cares about template inheritance, rebasing, updating variables and so on.
example.py
import stpl2
manager = stpl2.TemplateManager('template_folder')
template_iterator = manager.render('template', {'my_var': 'My variable'})
template_string = ''.join(template_iterator)
print(template_string)
template.tpl
% # my simple template Literal line {{ myvar }} % for i in range(100): {{ ! i }} % end
Generated python code
# -*- coding: UTF-8 -*-
def __template__():
# my simple template #lineno:1#
yield ( #lineno:2#
'Literal line\n'
'%s\n' #lineno:3#
) % (_escape(myvar),) #lineno:4#
for i in range(100):
yield ( #lineno:5#
' %s\n'
) % (i,) #lineno:6#
__blocks__ = {}
__includes__ = []
__extends__ = None
__rebase__ = None
Output
Literal line My variable 0 1 2 3 4
Loosy coupled means you can inherit any class and change any external code dependency, without dealing with hardcoded cross-dependencies on classes, or functions.
Nearly all code lines are covered by tests.
This benchmarks' code are based on Andriy Kornatskyy (akorn) benchmark suite, adding bottle and stpl2 and removing mako, wheezy and tenjin (which seems to use some hacks which break other engines).
cpython 3.4.1
Note: bottle cannot run inheritance benchmarks due missing support.
cpython 2.7.6
pypy 2.3.1 (python 2.7.6)
Note: tornado does not run on pypy.
A different approach which delivers the same speed (a bit faster in some cases), but with a maintenable and extendible codebase.
TemplateRuntimeError prints a traceback pointing to original template code, and the exception object comes with userful debug info (line number and code for both python and original template code).
This project supports bottle.py 0.2 and 0.3 template syntax, and can be used as a drop-in replace.
Stpl2 allows extends/block based template inheritance like other bigger template engines.
base.tpl
% block my_block My base block content. % end
template.tpl
% extends base % block my_block Base: {{ block.super }} My inherited block content. % end
output
Base: My base block content. My inherited block content.
base.tpl
My first line {{ base }} My third line
rebase.tpl
% rebase base My second line
output
My first line My second line My third line
include.tpl
External line
template.tpl
First line % include include Last line
output
First line External line Last line
import stpl2
manager = stpl2.TemplateManager(directories=['path/to/templates', 'more/templates'])
# template lookup
template = manager.get_template('template.tpl')
# add template from string
manager.templates['template2.tpl'] = stpl2.Template('Hello world, {{ name }}.', manager=manager)
# rendering generator from manager
template_generator = manager.render('template.tpl', {'foo': 'bar'})
# rendering generator from template
template_generator = template.render({'foo': 'bar'})
# render and print template
print(''.join(template_generator))
# print template code and generated python code (useful for debugging)
print(template.code)
print(template.pycode)
Default template behavior is to stream templates using yield without worrying about buffering. This approach have been choosen due most wsgi or proxy servers tends to buffer the responses themselves.
If buffering is a must for you, BufferingTemplate can be used, inheriting from TemplateManager class and overriding its template_class attribute.
BufferingTemplate can be customized in the same way in order to change the buffer size (the size of yielded chunks in bytes).
import stpl2
class BufferingTemplate(stpl2.BufferingTemplate):
buffersize = 3048 # buffering size in bytes
class BufferingTemplateManager(stpl2.TemplateManager):
template_class = BufferingTemplate
Using yield has a side effect, when you want a string you must join the generator object returned by render.
import stpl2
manager = stpl2.TemplateManager('template_folder')
template_generator = manager.render("my_template", {"template_variable":2})
template_string = ''.join(template_iterator)