aosabook/500lines · error · TempliteSyntaxError

%s: %r

Error message

%s: %r

What it means

Raised by Templite._syntax_error, the single chokepoint for all template-compilation errors. It formats a human-readable msg and the offending token via '%s: %r'. TempliteSyntaxError subclasses ValueError and is raised during Templite() construction (compile time), never at render time. Possible messages come from the parser: invalid variable names, malformed if/for/endif/endfor tags, too many ends, mismatched end tags, unknown tags, and unmatched action tags.

Source

Thrown at template-engine/code/templite.py:217

        if "|" in expr:
            pipes = expr.split("|")
            code = self._expr_code(pipes[0])
            for func in pipes[1:]:
                self._variable(func, self.all_vars)
                code = "c_%s(%s)" % (func, code)
        elif "." in expr:
            dots = expr.split(".")
            code = self._expr_code(dots[0])
            args = ", ".join(repr(d) for d in dots[1:])
            code = "do_dots(%s, %s)" % (code, args)
        else:
            self._variable(expr, self.all_vars)
            code = "c_%s" % expr
        return code

    def _syntax_error(self, msg, thing):
        """Raise a syntax error using `msg`, and showing `thing`."""
        raise TempliteSyntaxError("%s: %r" % (msg, thing))

    def _variable(self, name, vars_set):
        """Track that `name` is used as a variable.

        Adds the name to `vars_set`, a set of variable names.

        Raises an syntax error if `name` is not a valid name.

        """
        if not re.match(r"[_a-zA-Z][_a-zA-Z0-9]*$", name):
            self._syntax_error("Not a valid name", name)
        vars_set.add(name)

    def render(self, context=None):
        """Render this template by applying it to `context`.

        `context` is a dictionary of values to use in this rendering.

View on GitHub (pinned to fba689d101)

Solutions

  1. Read the message: it names the problem ('Not a valid name', 'Don't understand for', etc.) and shows the token via %r.
  2. Make variable names match [_a-zA-Z][_a-zA-Z0-9]* (no hyphens, dots, or spaces).
  3. Match every {% if %}/{% for %} with exactly one {% endif %}/{% endfor %} of the matching type and add no extra ends.
  4. Remember only if/for/endif/endfor are supported; remove unsupported constructs.

Example fix

// before
Templite('Hi {{user-name}}')              # Not a valid name: 'user-name'
Templite('{% if a b %}x{% endif %}')      # Don't understand if
// after
Templite('Hi {{user_name}}')
Templite('{% if a %}x{% endif %}')
Defensive patterns

Strategy: try-catch

Validate before calling

import re
NAME_RE = re.compile(r'[_a-zA-Z][_a-zA-Z0-9]*$')

def looks_valid(template):
    for m in re.findall(r'{{(.+?)}}', template):
        for part in m.split('|'):
            head = part.split('.')[0].strip()
            if head and not NAME_RE.match(head):
                return False, head
    return True, None

Try / catch

try:
    t = Templite(template_text)
except TempliteSyntaxError as e:
    log.error('bad template: %s', e)
    t = None

Prevention

When it happens

Trigger: Constructing Templite(text) where text contains: a variable name failing the regex [_a-zA-Z][_a-zA-Z0-9]*$ (e.g. 'user-name'); an {% if %} without exactly one expression; a {% for %} not matching 'for var in expr'; extra {% endif %}/{% endfor %}; an {% endfor %} after an {% if %}; an unsupported {% tag %}; or a missing end tag at EOF.

Common situations: Hand-written templates with typos; copy-pasting Django syntax this nano-subset does not support (no {% else %}, no filter arguments); forgetting to close an {% if %}/{% for %}; using hyphens, dots, or spaces in variable names.

Related errors


AI-assisted analysis of aosabook/500lines@fba689d101 (2026-08-13). Data as JSON: /api/errors/e042fa40540730e2. Report an issue: GitHub.