Pyjo.Template - Python-ish templates¶
from Pyjo.Template import Template
# Use Python modules
mt = Template()
print(mt.render('''
% import time
<div>
% now = time.gmtime(0)
Time: <%= time.strftime('%H:%M:%S', now) %>
</div>
'''))
# Or import the module, as in Google Python Style Guide
import Pyjo.Template
# Use Python modules
mt = Pyjo.Template.new()
print(mt.render('''
% import time
<div>
% now = time.gmtime(0)
Time: <%= time.strftime('%H:%M:%S', now) %>
</div>
'''))
# Render with arguments
print(mt.render('''
% numbers, title = args
<div>
<h1><%= title %></h1>
% for i in numbers {
Test <%= i %>
% }
</div>
''', range(1, 14), 'Hello World!'))
# Render with named variables
print(Pyjo.Template.new(vars=True).render('''
<div>
<h1><%= title %></h1>
%= 5 + 5
</div>
''', {'title': 'Hello World!'}))
Pyjo.Template is a minimalistic, fast, and very Python-ish template
engine, designed specifically for all those small tasks that come up during
big projects. Like preprocessing a configuration file, generating text from
heredocs and stuff like that.
Syntax¶
<% Python code %>
<%= Python expression, replaced with result %>
<%== Python expression, replaced with XML escaped result %>
<%# Comment, useful for debugging %>
<%% Replaced with "<%", useful for generating templates %>
% Python code line, treated as "<% line =%>" (explained later)
%= Python expression line, treated as "<%= line %>"
%== Python expression line, treated as "<%== line %>"
%# Comment line, useful for debugging
%% Replaced with "%", useful for generating templates
Escaping behavior can be reversed with the auto_escape
attribute.
<%= Python expression, replaced with XML escaped result %>
<%== Python expression, replaced with result %>
Markup, an object with the method __html__, as Pyjo.String.HTML,
is always excluded from automatic escaping, and a value is rendered as an
interpolation of Pyjo.String.Template: None is nothing, a template
is markup, and a list is rendered item by item.
% from Pyjo.String.HTML import html
<%= html('<div>excluded!</div>') %>
The indentation of the template does not matter: a code line that ends with
: opens a block, which end closes, and a line that starts with
elif, else, except, finally or case closes the block
before it and opens the next one. end closes the whole match after its
last case. Braces can open and close a block too, which makes
one-liners easy: { at the end of a line that starts with a keyword of a
block opens it, and } at the start of a line closes it, as in } else
{. Any other brace is the one of Python, so a dict or a set that
starts at the end of such a line is put in parentheses.
% for name in ['Baerbel', 'Wolfgang']:
% if name.startswith('B'):
<b><%= name %></b>
% else:
<i><%= name %></i>
% end
% end
<% for i in range(3) { %><%= i %><% } %>
Whitespace characters around tags can be trimmed by adding an additional equal sign to the end of a tag.
<% for i in range(1, 4) { %>
<%= 'Trim all whitespace characters around this expression' =%>
<% } %>
Newline characters can be escaped with a backslash.
This is <%= 1 + 1 %> a\
single line
And a backslash in front of a newline character can be escaped with another backslash.
This will <%= 1 + 1 %> result\\
in multiple\\
lines
A newline character gets appended automatically to every template, unless the last character is a backslash. And empty lines at the end of a template are ignored.
There is <%= 1 + 1 %> no newline at the end here\
You can capture whole template blocks for reuse later with the begin and
end keywords. Just be aware that both keywords are part of the surrounding
tag and not actual Python code, so there can only be whitespace after
begin and before end.
<% block = begin %>
<% name = args[0] =%>
Hello <%= name %>.
<% end %>
<%= block('Baerbel') %>
<%= block('Wolfgang') %>
Python lines can also be indented freely.
% block = begin
% name = args[0]
Hello <%= name %>.
% end
%= block('Baerbel')
%= block('Wolfgang')
Pyjo.Template templates get compiled to a Python function, that means
you can access arguments simply via args.
% foo, bar = args
% x = args[0]
test 123 <%= foo %>
The compilation of templates to Python code can make debugging a bit tricky,
but Pyjo.Template will return Pyjo.Exception objects that
stringify to error messages with context.
name 'xx' is not defined at template line 4.
Context:
1: <html>
2: </head>
3: <body>
4: % i = 2; xx
5: %= i * 2
6: </body>
Traceback (most recent call first):
File "template", line 4, in "_template"
File "path/to/Pyjo/Template.py", line 123, in "process"
File "path/to/myapp.py", line 123, in "<module>"
Debugging¶
You can set the PYJO_TEMPLATE_DEBUG environment variable to get some
advanced diagnostics information printed to STDERR.
PYJO_TEMPLATE_DEBUG=1
See also¶
Pyjoyment, https://mojolicious.org.
Ported from Mojo::Template of Mojolicious 9.49.
Classes¶
- class Pyjo.Template.Pyjo_Template(*, append: str = '', auto_escape: bool | None = None, capture_end: str = 'end', capture_start: str = 'begin', code: str = '', comment_mark: str = '#', compiled: Callable[[...], Any] | None = None, encoding: str | None = 'UTF-8', escape: Callable[[Any], str] | None = None, escape_mark: str = '=', expression_mark: str = '=', line_start: str = '%', name: str | None = None, namespace: str = 'Pyjo.Template.Sandbox', prepend: str = '', replace_mark: str = '%', tag_end: str = '%>', tag_start: str = '<%', tree: list[list[str]] | None = None, trim_mark: str = '=', unparsed: str = '', vars: bool | None = None)¶
Pyjo.Templateinherits all methods fromPyjo.Baseand implements the following new ones.- append¶
code = mt.append mt.append = 'print("Processed template")'
Append Python code to compiled template, as lines of their own.
- auto_escape¶
boolean = mt.auto_escape mt.auto_escape = boolean
Activate automatic escaping.
# "<html>" Pyjo.Template.new(auto_escape=True).render("<%= '<html>' %>")
- capture_end¶
end = mt.capture_end mt.capture_end = 'end'
Keyword indicating the end of a capture block, defaults to
end.<% block = begin %> Some data! <% end %>
- capture_start¶
start = mt.capture_start mt.capture_start = 'begin'
Keyword indicating the start of a capture block, defaults to
begin.<% block = begin %> Some data! <% end %>
- code¶
code = mt.code mt.code = code
Python code for template if available.
- comment_mark¶
mark = mt.comment_mark mt.comment_mark = '#'
Character indicating the start of a comment, defaults to
#.<%# This is a comment %>
- compiled¶
compiled = mt.compiled mt.compiled = compiled
Compiled template code if available.
- encoding¶
encoding = mt.encoding mt.encoding = 'UTF-8'
Encoding used for template files, defaults to
UTF-8.
- escape: Callable[[Any], str]¶
cb = mt.escape mt.escape = lambda value: ...
A callback used to escape the results of escaped expressions, defaults to
Pyjo.String.Template.escape().mt.escape = lambda value: str(value)[::-1]
- escape_mark¶
mark = mt.escape_mark mt.escape_mark = '='
Character indicating the start of an escaped expression, defaults to
=.<%== foo %>
- expression_mark¶
mark = mt.expression_mark mt.expression_mark = '='
Character indicating the start of an expression, defaults to
=.<%= foo %>
- line_start¶
start = mt.line_start mt.line_start = '%'
Character indicating the start of a code line, defaults to
%.% foo = 23
- property name: str¶
name = mt.name mt.name = 'foo.ep'
Name of template currently being processed, defaults to
template.
- namespace¶
namespace = mt.namespace mt.namespace = 'main'
Namespace used to compile templates, defaults to
Pyjo.Template.Sandbox: the module of the name, if it is loaded, or a module of its own. Note that namespaces should only be shared very carefully between templates, since the globals of the module will not be cleared automatically.
- prepend¶
code = mt.prepend mt.prepend = 'this = args[0]'
Prepend Python code to compiled template, as lines of their own.
- process(*args: Anything) Any¶
output = mt.process() output = mt.process(*args) output = mt.process({'foo': 'bar'})
Process previously parsed template and return the result, or a
Pyjo.Exceptionobject if rendering failed.# Parse and process print(Pyjo.Template.new().parse('Hello <%= args[0] %>').process('Bender')) # Reuse template (for much better performance) mt = Pyjo.Template.new() print(mt.render('Hello <%= args[0] %>!', 'Bender')) print(mt.process('Fry')) print(mt.process('Leela'))
- render(template: str, *args: Anything) Any¶
output = mt.render('<%= 1 + 1 %>') output = mt.render('<%= args[0] + args[1] %>', *args) output = mt.render('<%= foo %>', {'foo': 'bar'})
Render template and return the result, or a
Pyjo.Exceptionobject if rendering failed.# Longer version output = mt.parse('<%= 1 + 1 %>').process() # Render with arguments print(Pyjo.Template.new().render('<%= args[0] %>', 'bar')) # Render with named variables print(Pyjo.Template.new(vars=True).render('<%= foo %>', {'foo': 'bar'}))
- render_file(path: Anything, *args: Anything) Any¶
output = mt.render_file('/tmp/foo.ep') output = mt.render_file('/tmp/foo.ep', *args) output = mt.render_file('/tmp/bar.ep', {'foo': 'bar'})
Same as
render(), but renders a template file.
- replace_mark¶
mark = mt.replace_mark mt.replace_mark = '%'
Character used for escaping the start of a tag or line, defaults to
%.<%% foo = 23 %>
- tag_end¶
end = mt.tag_end mt.tag_end = '%>'
Characters indicating the end of a tag, defaults to
%>.<%= foo %>
- tag_start¶
start = mt.tag_start mt.tag_start = '<%'
Characters indicating the start of a tag, defaults to
<%.<% foo = 23 %>
- tree: list[list[str]]¶
tree = mt.tree mt.tree = [['text', 'foo'], ['line']]
Template in parsed form if available. Note that this structure should only be used very carefully since it is very dynamic.
- trim_mark¶
mark = mt.trim_mark mt.trim_mark = '-'
Character activating automatic whitespace trimming, defaults to
=.<%= foo =%>
- unparsed¶
unparsed = mt.unparsed mt.unparsed = '<%= 1 + 1 %>'
Raw unparsed template if available.
- vars¶
boolean = mt.vars mt.vars = boolean
Instead of a list of values, use a dict with named variables to pass data to templates.
# "works!" Pyjo.Template.new(vars=True).render('<%= test %>!', {'test': 'works'})