Skip to main content

templore

A from-scratch, zero-dependency Jinja2-like template engine in pure Python. Written to actually understand how a real template engine works end to end: lexing → expression parsing → AST → tree-walking rendering → inheritance.

No pip install of anything — the whole engine is standard-library only.

from templore import Environment, DictLoader

env = Environment(loader=DictLoader({"hi": "Hello {{ name|upper }}!"}))
print(env.get_template("hi").render(name="ada"))     # Hello ADA!

print(Environment().from_string("{{ 1 + 2 * 3 }}").render())  # 7

Features

Category What's supported
Output {{ expr }} with attribute (a.b), index (a[0]), slices (a[1:3], a[::-1]), calls (f(x))
Operators + - * / // % ~, == != < > <= >=, and or not, in / not in
Literals ints, floats, strings, true/false/none, lists [..], dicts {..}
Comprehensions [x*2 for x in xs if x], {k: v for k, v in pairs} (scoped, both backends)
Control flow {% if/elif/else %}, {% for %} (+ {% else %}, tuple unpacking)
Inline conditional {{ a if cond else b }} (else optional)
Loop variable loop.index/index0/first/last/revindex/length/previtem/nextitem/cycle()
Assignment {% set x = expr %}, attribute/item targets ({% set ns.x = ... %}), block set ({% set x %}...{% endset %}, optionally filtered)
Mutable state namespace(...) for values that survive across loop iterations
Side effects {% do list.append(x) %}
Scoping {% with x = a, y = b %}...{% endwith %}
Macros {% macro f(a, b=default) %}...{% endmacro %}, called {{ f(x) }} (closes over scope)
Call blocks {% call f(x) %}body{% endcall %} — macro renders the body via caller()
Imports {% import 'lib' as ns %}, {% from 'lib' import a, b as c %} (reuse macros)
Filters | pipeline with args & chaining: {{ x|round(2)|string }} — 40+ built-ins
Filter blocks {% filter upper %}...{% endfilter %}
Tests {% if x is defined %}, is not none, is even, is divisibleby(3), is sameas(y), …
Comments {# ... #}
Line statements opt-in # for x in xs / ## comment via Environment(line_statement_prefix=...)
Raw {% raw %}...{% endraw %} (emit template syntax literally)
Whitespace control {%- ... -%} / {{- ... -}}
Inheritance {% extends %}, {% block %} (multi-level), {{ super() }}
Includes {% include %} (shares the caller's context)
Autoescaping opt-in HTML escaping with |safe / |escape and Markup; {% autoescape true/false %} block toggle
Undefined lenient (renders "") or strict mode
Error locations runtime errors carry the template name + line number (both backends)
Custom delimiters swap {{ }} / {% %} / {# #} for anything via Environment(delimiters=...)
Two backends tree-walking interpreter (default) or Environment(compiled=True) codegen (~2.5x faster)

Install / run

python examples/demo.py       # guided tour
python examples/benchmark.py  # interpreter vs compiled
python -m pytest -q           # 251 tests

# render a template file from the command line:
python -m templore render page.html --data ctx.json
python -m templore render page.html --var name=Ada --compiled -o out.html

API

Environment(loader=None, autoescape=False, undefined="lenient")
    .from_string(source, name=None) -> Template
    .get_template(name) -> Template          # via loader, cached

Template.render(context=None, **kwargs) -> str

Loaders: DictLoader({name: source}), FileSystemLoader(path_or_paths).

CLI

python -m templore render TEMPLATE [--data FILE.json] [--var K=V ...]
                                   [--compiled] [--autoescape] [-o OUT]

The template is loaded via a FileSystemLoader rooted at its own directory, so extends / include / import resolve relative to it. --var values are strings and override keys from --data.

How it works

The pipeline lives in templore/ as five small stages:

source ──Lexer──▶ tokens ──Parser──▶ AST ──Template.render──▶ output
                              │
                     ExpressionParser (recursive descent w/ precedence)
  • lexer.py — splits raw source on the {{ }} / {% %} / {# #} delimiters (applying - whitespace trimming), then tokenizes the expression text inside each tag.
  • expressions.py — a recursive-descent parser that turns expression tokens into Expr nodes, honoring operator precedence (or < and < not < compare < add < mul < unary < filter < postfix < primary).
  • parser.py — assembles statement nodes (for/if/set/block/…), recursing through nested bodies until it meets the matching end tag.
  • nodes.py — the AST. Expression nodes evaluate(ctx, env); statement nodes render(ctx, env). A plain tree-walking interpreter — no codegen.
  • environment.py — configuration, the template cache, and the inheritance engine. It flattens the extends chain into per-block "chains" so that overriding and super() fall out naturally.

Runtime bits (runtime.py): a scoped Context, the lenient Undefined sentinel, the Namespace for imports, and the Markup safe-string used by autoescaping. Filters and tests live in filters.py.

The compiled backend (compiler.py)

Environment(compiled=True) swaps the tree-walker for a code generator: the AST is turned into Python source for a root(ctx, env) function, exec'd once, and cached on the template. Expressions become inlined Python expressions and {% for %} / {% if %} become native loops/branches — no per-node dispatch at render time. Inspect it with template.compiled_root.__source__.

env = Environment(compiled=True)
t = env.from_string("{% for x in xs %}{{ x|upper }}{% endfor %}")
print(t.compiled_root.__source__)   # the generated Python

Templates using extends / block / macro / import / call are left to the interpreter (the compiler returns None and rendering falls back), so turning compiled=True on is always safe — output is identical either way, which the test suite verifies case-by-case.

License

MIT

Metadata

Release files for templore 1.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for templore 1.0.1
File Size Uploaded Provenance Actions
templore-1.0.1.tar.gz 41.6 kB Without provenance Details for templore-1.0.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for templore 1.0.1
File Interpreter ABI Platform Actions
templore-1.0.1-py3-none-any.whl Python 3 none any Details for templore-1.0.1-py3-none-any.whl

Total release size: 74.3 kB

Release files / templore-1.0.1.tar.gz

Download URL templore-1.0.1.tar.gz
Size 41.6 kB
Tags Source
SHA-256 checksum
How to use checksums
e5ebcdb0aa5084f73ad9d08d77af5b62044d24313c45c336471534369a486ad7
BLAKE2b-256 checksum
How to use checksums
ecc7f1845bc688ebc5074a3e757cd29097c440fcdf0f8838ca128bbdea99ce3a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.9

Release files / templore-1.0.1-py3-none-any.whl

Download URL templore-1.0.1-py3-none-any.whl
Size 32.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d1177d2f1c4a2f562793b94a4769b6eb7e53a6288244f0a3db41315186823a9f
BLAKE2b-256 checksum
How to use checksums
3446e699f0704ed8cd589ded55116c0257e6e4f85d5a7e3ad9c83fe074e26ba1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.9

Security for templore 1.0.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page