The Mako Runtime Environment¶
This section describes a little bit about the objects and built-in functions that are available in templates.
Context¶
The Context
is the central object that is created when
a template is first executed, and is responsible for handling
all communication with the outside world. Within the template
environment, it is available via the reserved name
context
. The Context
includes two
major components, one of which is the output buffer, which is a
file-like object such as Python’s StringIO
or similar, and
the other a dictionary of variables that can be freely
referenced within a template; this dictionary is a combination
of the arguments sent to the Template.render()
function and
some built-in variables provided by Mako’s runtime environment.
The Buffer¶
The buffer is stored within the Context
, and writing
to it is achieved by calling the Context.write()
method
– in a template this looks like context.write('some string')
.
You usually don’t need to care about this, as all text within a template, as
well as all expressions provided by ${}
, automatically send
everything to this method. The cases you might want to be aware
of its existence are if you are dealing with various
filtering/buffering scenarios, which are described in
Filtering and Buffering, or if you want to programmatically
send content to the output stream, such as within a <% %>
block.
<%
context.write("some programmatic text")
%>
The actual buffer may or may not be the original buffer sent to
the Context
object, as various filtering/caching
scenarios may “push” a new buffer onto the context’s underlying
buffer stack. For this reason, just stick with
context.write()
and content will always go to the topmost
buffer.
Context Variables¶
When your template is compiled into a Python module, the body
content is enclosed within a Python function called
render_body
. Other top-level defs defined in the template are
defined within their own function bodies which are named after
the def’s name with the prefix render_
(i.e. render_mydef
).
One of the first things that happens within these functions is
that all variable names that are referenced within the function
which are not defined in some other way (i.e. such as via
assignment, module level imports, etc.) are pulled from the
Context
object’s dictionary of variables. This is how you’re
able to freely reference variable names in a template which
automatically correspond to what was passed into the current
Context
.
What happens if I reference a variable name that is not in the current context? - The value you get back is a special value called
UNDEFINED
, or if thestrict_undefined=True
flag is used aNameError
is raised.UNDEFINED
is just a simple global variable with the classmako.runtime.Undefined
. TheUNDEFINED
object throws an error when you callstr()
on it, which is what happens if you try to use it in an expression.UNDEFINED makes it hard for me to find what name is missing - An alternative is to specify the option
strict_undefined=True
to theTemplate
orTemplateLookup
. This will cause any non-present variables to raise an immediateNameError
which includes the name of the variable in its message whenTemplate.render()
is called –UNDEFINED
is not used.New in version 0.3.6.
Why not just return None? Using
UNDEFINED
, or raising aNameError
is more explicit and allows differentiation between a value ofNone
that was explicitly passed to theContext
and a value that wasn’t present at all.Why raise an exception when you call str() on it ? Why not just return a blank string? - Mako tries to stick to the Python philosophy of “explicit is better than implicit”. In this case, it’s decided that the template author should be made to specifically handle a missing value rather than experiencing what may be a silent failure. Since
UNDEFINED
is a singleton object just like Python’sTrue
orFalse
, you can use theis
operator to check for it:% if someval is UNDEFINED: someval is: no value % else: someval is: ${someval} % endif
Another facet of the Context
is that its dictionary of
variables is immutable. Whatever is set when
Template.render()
is called is what stays. Of course, since
its Python, you can hack around this and change values in the
context’s internal dictionary, but this will probably will not
work as well as you’d think. The reason for this is that Mako in
many cases creates copies of the Context
object, which
get sent to various elements of the template and inheriting
templates used in an execution. So changing the value in your
local Context
will not necessarily make that value
available in other parts of the template’s execution. Examples
of where Mako creates copies of the Context
include
within top-level def calls from the main body of the template
(the context is used to propagate locally assigned variables
into the scope of defs; since in the template’s body they appear
as inlined functions, Mako tries to make them act that way), and
within an inheritance chain (each template in an inheritance
chain has a different notion of parent
and next
, which
are all stored in unique Context
instances).
So what if I want to set values that are global to everyone within a template request? - All you have to do is provide a dictionary to your
Context
when the template first runs, and everyone can just get/set variables from that. Lets say its calledattributes
.Running the template looks like:
output = template.render(attributes={})
Within a template, just reference the dictionary:
<% attributes['foo'] = 'bar' %> 'foo' attribute is: ${attributes['foo']}
Why can’t “attributes” be a built-in feature of the Context? - This is an area where Mako is trying to make as few decisions about your application as it possibly can. Perhaps you don’t want your templates to use this technique of assigning and sharing data, or perhaps you have a different notion of the names and kinds of data structures that should be passed around. Once again Mako would rather ask the user to be explicit.
Context Methods and Accessors¶
Significant members of Context
include:
context[key]
/context.get(key, default=None)
- dictionary-like accessors for the context. Normally, any variable you use in your template is automatically pulled from the context if it isn’t defined somewhere already. Use the dictionary accessor and/orget
method when you want a variable that is already defined somewhere else, such as in the local arguments sent to a%def
call. If a key is not present, like a dictionary it raisesKeyError
.keys()
- all the names defined within this context.kwargs
- this returns a copy of the context’s dictionary of variables. This is useful when you want to propagate the variables in the current context to a function as keyword arguments, i.e.:${next.body(**context.kwargs)}
write(text)
- write some text to the current output stream.lookup
- returns theTemplateLookup
instance that is used for all file-lookups within the current execution (even though individualTemplate
instances can conceivably have different instances of aTemplateLookup
, only theTemplateLookup
of the originally-calledTemplate
gets used in a particular execution).
The Loop Context¶
Within % for
blocks, the reserved name loop
is available. loop
tracks the progress of
the for
loop and makes it easy to use the iteration state to control
template behavior:
<ul>
% for a in ("one", "two", "three"):
<li>Item ${loop.index}: ${a}</li>
% endfor
</ul>
New in version 0.7.
Iterations¶
Regardless of the type of iterable you’re looping over, loop
always tracks
the 0-indexed iteration count (available at loop.index
), its parity
(through the loop.even
and loop.odd
bools), and loop.first
, a bool
indicating whether the loop is on its first iteration. If your iterable
provides a __len__
method, loop
also provides access to
a count of iterations remaining at loop.reverse_index
and loop.last
,
a bool indicating whether the loop is on its last iteration; accessing these
without __len__
will raise a TypeError
.
Cycling¶
Cycling is available regardless of whether the iterable you’re using provides
a __len__
method. Prior to Mako 0.7, you might have generated a simple
zebra striped list using enumerate
:
<ul>
% for i, item in enumerate(('spam', 'ham', 'eggs')):
<li class="${'odd' if i % 2 else 'even'}">${item}</li>
% endfor
</ul>
With loop.cycle
, you get the same results with cleaner code and less prep work:
<ul>
% for item in ('spam', 'ham', 'eggs'):
<li class="${loop.cycle('even', 'odd')}">${item}</li>
% endfor
</ul>
Both approaches produce output like the following:
<ul>
<li class="even">spam</li>
<li class="odd">ham</li>
<li class="even">eggs</li>
</ul>
Parent Loops¶
Loop contexts can also be transparently nested, and the Mako runtime will do
the right thing and manage the scope for you. You can access the parent loop
context through loop.parent
.
This allows you to reach all the way back up through the loop stack by
chaining parent
attribute accesses, i.e. loop.parent.parent....
as
long as the stack depth isn’t exceeded. For example, you can use the parent
loop to make a checkered table:
<table>
% for consonant in 'pbj':
<tr>
% for vowel in 'iou':
<td class="${'black' if (loop.parent.even == loop.even) else 'red'}">
${consonant + vowel}t
</td>
% endfor
</tr>
% endfor
</table>
<table>
<tr>
<td class="black">
pit
</td>
<td class="red">
pot
</td>
<td class="black">
put
</td>
</tr>
<tr>
<td class="red">
bit
</td>
<td class="black">
bot
</td>
<td class="red">
but
</td>
</tr>
<tr>
<td class="black">
jit
</td>
<td class="red">
jot
</td>
<td class="black">
jut
</td>
</tr>
</table>
Migrating Legacy Templates that Use the Word “loop”¶
Changed in version 0.7: The loop
name is now reserved in Mako,
which means a template that refers to a variable named loop
won’t function correctly when used in Mako 0.7.
To ease the transition for such systems, the feature can be disabled across the board for all templates, then re-enabled on a per-template basis for those templates which wish to make use of the new system.
First, the enable_loop=False
flag is passed to either the TemplateLookup
or Template
object in use:
lookup = TemplateLookup(directories=['/docs'], enable_loop=False)
or:
template = Template("some template", enable_loop=False)
An individual template can make usage of the feature when enable_loop
is set to
False
by switching it back on within the <%page>
tag:
<%page enable_loop="True"/>
% for i in collection:
${i} ${loop.index}
% endfor
Using the above scheme, it’s safe to pass the name loop
to the Template.render()
method as well as to freely make usage of a variable named loop
within a template, provided
the <%page>
tag doesn’t override it. New templates that want to use the loop
context
can then set up <%page enable_loop="True"/>
to use the new feature without affecting
old templates.
All the Built-in Names¶
A one-stop shop for all the names Mako defines. Most of these
names are instances of Namespace
, which are described
in the next section, Namespaces. Also, most of
these names other than context
, UNDEFINED
, and loop
are
also present within the Context
itself. The names
context
, loop
and UNDEFINED
themselves can’t be passed
to the context and can’t be substituted – see the section Reserved Names.
context
- this is theContext
object, introduced at Context.local
- the namespace of the current template, described in Built-in Namespaces.self
- the namespace of the topmost template in an inheritance chain (if any, otherwise the same aslocal
), mostly described in Inheritance.parent
- the namespace of the parent template in an inheritance chain (otherwise undefined); see Inheritance.next
- the namespace of the next template in an inheritance chain (otherwise undefined); see Inheritance.caller
- a “mini” namespace created when using the<%call>
tag to define a “def call with content”; described in Calling a Def with Embedded Content and/or Other Defs.loop
- this provides access toLoopContext
objects when they are requested within% for
loops, introduced at The Loop Context.capture
- a function that calls a given def and captures its resulting content into a string, which is returned. Usage is described in Filtering and Buffering.UNDEFINED
- a global singleton that is applied to all otherwise uninitialized template variables that were not located within theContext
when rendering began, unless theTemplate
flagstrict_undefined
is set toTrue
.UNDEFINED
is an instance ofUndefined
, and raises an exception when its__str__()
method is called.pageargs
- this is a dictionary which is present in a template which does not define any**kwargs
section in its<%page>
tag. All keyword arguments sent to thebody()
function of a template (when used via namespaces) go here by default unless otherwise defined as a page argument. If this makes no sense, it shouldn’t; read the section The body() Method.
Reserved Names¶
Mako has a few names that are considered to be “reserved” and can’t be used as variable names.
Changed in version 0.7: Mako raises an error if these words are found passed to the template as context arguments, whereas in previous versions they’d be silently ignored or lead to other error messages.
context
- see Context.UNDEFINED
- see Context Variables.loop
- see The Loop Context. Note this can be disabled for legacy templates via theenable_loop=False
argument; see Migrating Legacy Templates that Use the Word “loop”.
API Reference¶
Object Name | Description |
---|---|
Bases: |
|
Bases: |
|
Bases: |
- class mako.runtime.Context(buffer, **data)¶
Bases:
object
Provides runtime namespace, output buffer, and various callstacks for templates.
See The Mako Runtime Environment for detail on the usage of
Context
.-
method
mako.runtime.Context.
get(key, default=None)¶ Return a value from this
Context
.
-
method
mako.runtime.Context.
keys()¶ Return a list of all names established in this
Context
.
-
attribute
mako.runtime.Context.
kwargs¶ Return the dictionary of top level keyword arguments associated with this
Context
.This dictionary only includes the top-level arguments passed to
Template.render()
. It does not include names produced within the template execution such as local variable names or special names such asself
,next
, etc.The purpose of this dictionary is primarily for the case that a
Template
accepts arguments via its<%page>
tag, which are normally expected to be passed viaTemplate.render()
, except the template is being called in an inheritance context, using thebody()
method.Context.kwargs
can then be used to propagate these arguments to the inheriting template:${next.body(**context.kwargs)}
-
attribute
mako.runtime.Context.
lookup¶ Return the
TemplateLookup
associated with thisContext
.
-
method
mako.runtime.Context.
pop_caller()¶ Pop a
caller
callable onto the callstack for thisContext
.
-
method
mako.runtime.Context.
push_caller(caller)¶ Push a
caller
callable onto the callstack for thisContext
.
-
method
mako.runtime.Context.
write(string)¶ Write a string to this
Context
object’s underlying output buffer.
-
method
mako.runtime.Context.
writer()¶ Return the current writer function.
-
method
- class mako.runtime.LoopContext(iterable)¶
Bases:
object
A magic loop variable. Automatically accessible in any
% for
block.See the section The Loop Context for usage notes.
parent
->LoopContext
orNone
The parent loop, if one exists.
index
-> intThe 0-based iteration count.
reverse_index
-> intThe number of iterations remaining.
first
-> boolTrue
on the first iteration,False
otherwise.last
-> boolTrue
on the last iteration,False
otherwise.even
-> boolTrue
whenindex
is even.odd
-> boolTrue
whenindex
is odd.
-
method
mako.runtime.LoopContext.
cycle(*values)¶ Cycle through values as the loop progresses.
- class mako.runtime.Undefined¶
Bases:
object
Represents an undefined value in a template.
All template modules have a constant value
UNDEFINED
present which is an instance of this object.