Template API
Template Objects
A template starts life as a simple string containing template markup, usually stored in the form of a text file. This string is used to initialize a Template
object:
>>> import ibis >>> template = ibis.Template('{{foo}} and {{bar}}')
Internally, a lexer transforms the string into a sequence of tokens. A parser then takes this
sequence and compiles it into a tree of nodes. Each node has a .render()
method which takes a
context object and returns a string. The entire compiled node tree can be rendered by calling .render()
on the root node.
Compiling and rendering the node tree are two distinct operations. The template only needs to be compiled once; it can then be cached and rendered multiple times with different context objects.
The Template
class acts as the public interface to the template engine. This is the only class the end-user needs to interact with directly.
A Template
object is initialized with a template string. It compiles the string and stores the
resulting node tree for future rendering. Calling the template object's .render()
method with a dictionary of key-value pairs renders the template and returns the result as a string.
>>> template.render({'foo': 'ham', 'bar': 'eggs'}) 'ham and eggs' >>> template.render({'foo': 1, 'bar': 2}) '1 and 2'
Template IDs
The Template
constructor takes an optional template_id
argument, an arbitrary string which is
used to identify the template in error messages.
template = ibis.Template(template_string, "template.txt")
If you've loaded the template from a file, the filename is the logical choice to use.
Template Loaders
If you want to use template inheritance or the {% include %}
tag, you'll
need to specify a template loader so Ibis can locate the appropriate templates to include or
extend.
A template loader is any callable that accepts a filename argument and either returns
a corresponding Template
object or raises a TemplateLoadError
exception.
You can define your own custom template loader, but in most cases an instance of the builtin FileLoader
class will be suitable. A FileLoader
instance is initialized with a path to a base template directory:
loader = ibis.loaders.FileLoader('/path/to/base/dir')
You can call the loader object with a filename argument which it interprets as a path to a UTF-8 encoded template file stored in the base directory:
template = loader('template.txt')
A FileLoader
instance compiles its templates once and caches them in memory for future lookups.
A FileReloader
instance is similar but will automatically reload and recompile a template if the underlying template file changes.
To specify a template loader set ibis.loader
to an instance of your callable:
ibis.loader = ibis.loaders.FileLoader('/path/to/base/dir')
Note that both FileLoader
and FileReloader
support multiple base directories:
ibis.loader = ibis.loaders.FileLoader( '/path/to/base/dir1', '/path/to/base/dir2', )
The Undefined Type
An instance of the ibis.context.Undefined
type is returned whenever an attempt to resolve a variable name against a particular context fails. Undefined
is a null type that renders as an empty string, evaluates as the boolean False
, behaves like an empty sequence, etc.
You can check if a variable is defined using the boolean is_defined
filter:
{% if foo.bar|is_defined %} do stuff with {{ foo.bar }} {% endif %}
You can specify a fallback value for undefined variables using the if_undefined
filter
{{ foo.bar|if_undefined("baz") }}
In this case if the variable foo.bar
is not defined in the current context, the fallback value "baz"
will be used in its place.
Strict Mode
You can render a template in strict mode by adding a strict_mode=True
keyword argument to the
.render()
method:
output = template.render(data, strict_mode=True)
In strict mode attempting to resolve an undefined variable will raise an UndefinedVariable
exception instead of returning an instance of the Undefined
type.
The is_defined
and if_undefined
filters won't work in strict mode (the exception will already
have been raised before the filter is called) but you can use the is_defined()
function to check if a variable is defined:
{% if is_defined("post.title") %} .. do something with {{ post.title }} ... {% endif %}
Builtins
The following built-in variables and functions are available in all contexts:
-
is_defined(variable)
Boolean function. Returns true if the specified variable is defined in the current context. The variable name should be supplied as a quoted string literal, e.g.
{% if is_defined("post.title") %} ... do something with {{ post.title }} ... {% endif %}
-
now()
Returns a datetime object representing the instant the function is called.
-
range()
Provides access to Python's builtin
range
function. -
context
A reference to the current
Context
object.
Exceptions
The following exception types may be raised by the template engine:
-
ibis.errors.TemplateError
Base class for all exceptions raised by the template engine.
-
ibis.errors.TemplateLoadError
Raised if an error occurs while attempting to load a template.
-
ibis.errors.TemplateLexingError
Raised if the lexer cannot tokenize a template string.
-
ibis.errors.TemplateSyntaxError
Raised if an error occurs while compiling a template.
-
ibis.errors.TemplateRenderingError
Raised if an error occurs while rendering a template.
-
ibis.errors.UndefinedVariable
Raised in strict mode if a variable name cannot be resolved.
-