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:

  1. escape customized to disable HTML-escaping.

  2. stringify replaces for JSON encoding.

  3. tags customized 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:

  1. Your template would need to be of type bytes for the rendering output to be also in bytes.

  2. Ensure your string variables are either bytes or expected to be encoded as utf-8.

  3. virtuals can be used as encoding and utility helpers.

  4. stringify can 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)