Mako is a template engine in Python. I choose it as it uses python which I am familiar, author still update till Nov. 2012, and author seems has consulted some other engines in making this one.
I only intend to use it to create some static web pages, so it should be sufficient to use.
http://docs.makotemplates.org/en/latest/usage.html
Template:
something can be rendered given some name/values;
can be loaded from file and cache python module in a dir
accepts output_encoding, and render_unicode() can render unicode
when compiled into a Python module, the body content is enclosed within a Python function called render_body
TemplateLookup:
something able to get a template from a given uri
specify a root, and optionally a cache for the module
accepts output_encoding
Context:
contain name/value pairs to be rendered; context is immutable, which is a good practice; if wish to change something, put a dict in the context;
Context strict undefined: if not defined, will always get UNDEFINED but not an error; if this make things difficult, specify the option strict_undefined=True to the Template or TemplateLookup
Exceptions:
there is compile exception and run-time exception
Defs and Blocks:
exist in generated python as a callable function, i.e. a puthon def; they differ as follows
Def: is more like python def; require a name;
- has access to current context space and can directly use the variables inside;
- <%def name="account(accountname, type='regular')"> defines parameters following python parameter requirements;
this is different than contextual variable which is UNDEFINED if not defined;
- Top-level defs are defined within template python code named render_name-of-def> and <% (i.e. render_mydef)
- Def can be placed within another Def;
Call Def: Call local def like ${hello()};
Call Def from another template: To import from another template like <%namespace name="mystuff" file="mystuff.html"/>; to call like ${mystuff.somedef(x=5,y=7)}; <%namespace file="mystuff.html" import="foo, bar"/> like "import... from" in python
Call Def problematically: print template.get_def("hi").render(name="ed"); Def is a DefTemplate;
Block: is more layout oriented; it's called immediately, doesn't require a name and doesn't allow parameter; block without modifier has no effect; block can specify caching directvie, filtering;
Named Block: can be overridden through inheritance; name is global regardless how deeply nested; can be called repeatedly; cannot be nested inside a def or the like;
Named Block referring page argument: named block are global; page argument needs to be passed to them, like <%block name="post_prose" args="post"> where "post" is a page argument; otherwise use ${pageargs['post'].content}
Substitution
Escaping value
If
For in
% escaping
Comment
Line-continual
Python block
Tags
- page
- include
- def
- block
- namespace
- inherit
- nsname:defname
- call (old fashion)
- text
- return
${x}
${pow(x,2) + pow(y,2)}
${"this is some text" | u}
% if x==5:
% endif
% for a in [1,2]:
<li>Item ${loop.index}:
% endfor
%%
## this is a comment.
<%doc> some other comment
</%doc>
line that goes onto \
next line
<%
%>
<%!
>%
<%include file="foo.txt"/>
and other tags
substitution, expression ok; if not otherwise defined (parameter, import, etc.), goes to resolve from context, and if still not found, UNDEFINED; if option strict_undefined=True specified to the Template or TemplateLookup, error will raise
url escaping; other escaping available, see http://docs.makotemplates.org/en/latest/filtering.html
"if" control structure
"for in"
About loop context: http://docs.makotemplates.org/en/latest/runtime.html#loop-context
actual %
end \ goes to the next
python block;
"'patentadm@eastip.com'" <patentadm@eastip.com>
<%! indicates module-level block, executed just once as template loads (at the module level, not the context of running, so put import and function definitions here...
tages, similar to xml (start with % and require closing); many attribute support substitute like file="/foo/bar/${myfile}.txt";
Name spaces correspond to a template, are like packages in python, group defs and can be imported
Def: just some def in a file
Namespace: import another template to use its def(s) or import those def(s) from another template
Call namespace from template:
Call namespace from python module:
[ I am skipping this part until I need to do it; when needs, refer http://docs.makotemplates.org/en/latest/namespaces.html]
Override Defs in Namespace: just declare defs inside namespace tag to override original defs in the namespace
body() method: render the entire body; by default take no argument and put all variable in the pageargs dictionary; if wish to take arguments, define specifically at the template such as:
<%page args="x, y, someval=8, scope='foo', **kwargs"/>
When called (for example in render()), the argument, if not given, will be pulled from context
Namespace attribute: the "attr" of a namespace (for example self) refers to the module level variables (see <%! )
Inheritance uses <%block>; general paradigm: C[Child] inherits P[Parent], then C passes control to P on render and P decides what resources shall be obtained from C (think of C is a subclass of P and does not have a render method but inherits the render method from P)
The idea is similar to python inheritance but not exactly.
- where P has a block, it invokes self's block, which is the topmost one's block, i.e. C's block if C has that block
- where P calls ${self.body()}, it calls the topmost body(), which is C's body
- during body() call which calls C, the block in C will not render as P has the same block, "a block only renders in its basemost scope".
Block in P can be nested and defined in C nested or not.
Rendering block multiple times - just use named block and call it like ${self.title()}
Context: within template it's available as name "context"
partial doc below, complete doc see (http://docs.makotemplates.org/en/latest/runtime.html#mako.runtime.Context)
LoopContext: within a %for block always under name "loop", usage such as
- ${loop.index}
- ${loop.cycle('even', 'odd')} : cycle through values to produce stripe
- loop.parent.parent.parent... : for nested loops
List of reserved, built in names (shamelessly copied, mostly, from the manual):
context
local - the namespace of the current template; particularly useful properties like uri, filename, method like module and get_namespace
self - the namespace of the topmost (contrary to base) template in an inheritance chain (if any, otherwise the same as local)
parent - the namespace of the parent template in an inheritance chain, like python "super" (otherwise undefined)
next - used in base template, the namespace of next template of inheritance chain towards topmost, while topmost is self
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
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 - the global singleton
pageargs -dictionary in template without any **kwargs section in <%page> tag. All keyword arguments sent to the body() function of a template (when used via namespaces) go here by default unless otherwise defined as a page argument. See The body() Method.
Chain processor of text, some are predefined, some can be defined as a def accepting a text and return a text; of course it can be imported;
<%!
def myescape(text):
return "<TAG>" + text + "</TAG>"
%>
Here's some tagged text: ${"text" | myescape}
Template-wide and TemplateLookup wide default filter for all expression: <%page expression_filter="h"/> in page, or "t = TemplateLookup(directories=['/tmp'], default_filters=['unicode'])" for Template and TemplateLookup; in case wishes to disable these default filters, put a "n" filter at the first filter
Not using right now so skipping
Decorator is called with a context (for writing, for example) and a function (a def, for example), and can "decorate" the function - add further function or replace it, not call it, etc. See
http://docs.makotemplates.org/en/latest/filtering.html
Not at this time, skipping
Not at this time, skipping
when there is problem and reason unclear, catch the exception and through a more meaningful one, like:
<%
try:
x = context.get('ms_root').get('news_publications').get(newsi).get('title')
except Exception as e:
raise Exception("processing "+newsi+" get problem, details: "+str(e))
%>
it is actually VERY DIFFICULT is not IMPOSSIBLE to judge a variable exist in python block, so python block actually not quite python...
Tips: just print in python block, it will be output to console and help so much...
it's painful to write a complicated template and was told some UNDEFINED error without name, only a line number in the compiled code not knowing where - so, my strategy is to add one line at a time, then test and run. this is the pitiful effect of a dynamic language
Mako Static is my small project to generate static contents with mako template. See: