Non-HTML use-cases¶
Mustache templates are, by spec, expected to autoescape especial HTML characters from variables, unless a raw variable is used.
From the official manual:
All variables are HTML escaped by default. If you want to return raw contents without escaping, use the triple mustache: {{{name}}}.
You can also use & to return its raw contents: {{& name}}.
mstache is highly customizable and can be adapted for non-HTML use-cases not covered by the reference specs and, so, rarely supported by any other implementation. This page explains a few use-case scenarios.
JSON Template¶
The following is an example on how integrate Mustache templating in a JSON context, the customizations made are:
escapecustomized to disable HTML-escaping.stringifyreplaces for JSON encoding.tagscustomized to include quotes, replacing whole strings with JSON.
import json
import textwrap
import mstache
print(mstache.render(
textwrap.dedent('''
{
"number": "{{int_value}}",
"string": "{{str_value}}",
"object": "{{dict_value}}"
}
'''),
{
'int_value': 1,
'str_value': 'string',
'dict_value': {},
},
stringify=lambda data, text: (
# JSONify values
json.dumps(data).encode()
),
escape=lambda x: x, # omit any escaping
tags=('"{{', '}}"'), # mstache tags to replace entire strings
))
# {
# "number": 1,
# "string": "string",
# "object": {}
# }
Binary template¶
mstache is one of the few Mustache implementations supporting binary templates, if any at all, where every byte is representative.
This is thanks to both its support for bytes templates and high
customizability. For binary templates you will need to care of the following:
Your template would need to be of type
bytesfor the rendering output to be also inbytes.Ensure your string variables are either
bytesor expected to be encoded asutf-8.virtualscan be used as encoding and utility helpers.stringifycan be used to customize how objects are turned into bytes.
Binary template example:
import functools
import json
import mstache
output = mstache.render(
(
# bytes template
b'{{data.length_uint32}}{{data}}'
b'{{integer.as_uint32}}'
),
{
'data': 'hello world!\nsome bytes', # to be utf8-encoded
'integer': 5, # to be encoded as 4-byte big-endian uint
},
getter=functools.partial(
mstache.default_getter,
virtuals=mstache.default_virtuals | {
# virtual byte helpers
'length_uint32': lambda x: (
# byte-length as 4-byte big-endian uint
len(x.encode() if isinstance(x, str) else x)
.to_bytes(4)
),
'as_uint32': lambda x: (
# value as 4-byte big-endian uint
int(x).to_bytes(4)
),
},
),
escape=lambda x: x, # omit any escaping
keep_lines=True, # do not collapse block lines
)
print(output)
# b'\x00\x00\x00\x17hello world!\nsome bytes\x00\x00\x00\x05'
# addendum: extraction
size = int.from_bytes(output[:4])
print((size, output[4:4 + size], int.from_bytes(output[-4:])))
# (23, b'hello world!\nsome bytes', 5)