tplx
Render a tree of templates where each file's suffix picks its engine and its
stem names what it renders: boot.cfg.j2 is Jinja, boot.cfg.mako is mako,
and both answer to boot.cfg. A file no engine claims is copied byte for byte.
Documentation: https://jose-pr.github.io/tplx/.
Features
- One call for every engine — Jinja, mako, Liquid, Handlebars, Mustache,
ERB, EPP and a
%{NAME}shell-style engine, each rendering the same data. - Name by stem, dispatch by suffix — a caller asks for
boot.cfg; the file found decides the engine. - Several search roots, local or remote, searched in order, plus
per-caller candidate names and paths (
web01.boot.cfgbeforeboot.cfg). - Includes that search the same way —
{% include %},<%include/>,{{> partial}}and the rest resolve through the lookup that loaded the file. - One undefined-variable policy (
strict,lenient,debug) that means the same thing in every engine that can honour it. - Your encoding of the data — a
DataEncodersubclass decides what is a structure and how values are written and flattened for the engines that cannot run Python. - Byte-exact fallback — an unclaimed file is copied as it is, never decoded.
- Pluggable — an engine is a class with a suffix list and a priority,
registered directly or through the
tplx.enginesentry point, and checked by the shippedtplx.testing.EngineContractsuite. - Nothing required — each engine's library is an extra, imported only when a file needs it.
Installation
pip install tplx
| Engine | Suffixes | Install | Undefined names | Includes |
|---|---|---|---|---|
| Jinja | .j2 .jinja .jinja2 |
tplx[jinja] |
strict, lenient, debug | {% include %} {% extends %} {% import %} {% from %} |
| mako | .mako |
tplx[mako] |
strict, lenient (debug is lenient) |
<%include/> <%inherit/> <%namespace/> |
| Liquid | .liquid |
tplx[liquid] |
strict, lenient, debug | {% include %} {% render %} |
| Handlebars | .hbs .handlebars |
tplx[handlebars] |
always empty | {{> name}} |
| Mustache | .mustache |
tplx[mustache] |
always empty | {{> name}} |
| ERB | .erb |
ruby on PATH |
Ruby's nil |
— |
| EPP | .epp |
puppet on PATH |
Puppet's undef |
— |
shell (%{NAME}) |
.shtpl |
— | strict, lenient, debug | — |
| copy | any other | — | n/a: the bytes as stored | — |
Each library-backed engine is an extra of that name (pip install "tplx[jinja,mako]"); the cli extra adds the tplx command.
Each engine keeps its own syntax and defaults. In particular Handlebars and
Mustache HTML-escape {{x}} (& becomes &), as their specifications
say; write {{{x}}} for the value as it is. The other engines write values
unescaped.
Templates and their roots are trusted, in every engine alike: Jinja and
mako run Python, ERB and EPP run Ruby and Puppet. What tplx guards is the
template name: one that would leave the search paths (.., a backslash,
a drive) is refused.
Quick start
from tplx import Loader
loader = Loader(["templates"])
text = loader.render("boot.cfg", {"hostname": "web01"}, release="13")
A caller with its own search order binds it once; includes follow it:
lookup = loader.lookup(
candidates=lambda name: [f"web01.{name}", name],
paths=["debian"], # templates/debian/ before templates/
)
text = lookup.render("boot.cfg", hostname="web01")
Filters, helpers and options go to each engine's own environment:
import shlex
from tplx import JinjaTemplate, Loader
loader = Loader(
["templates"],
configure={JinjaTemplate: lambda env: env.filters.update(quote=shlex.quote)},
)
Command line
pip install "tplx[cli,jinja]"
tplx render templates/boot.cfg.j2 -s hostname=web01 # one file
tplx render boot.cfg -r templates -d host.json -o boot.cfg # a name, data from JSON
tplx render web01.boot.cfg boot.cfg -r templates # the first name found
tplx find boot.cfg -r templates # which file, which engine
tplx engines # what is registered
-s a.b=v nests and merges into --data; -s n=json:3 passes JSON. Exit
status: 0 done, 1 the template or an engine failed, 2 a usage error.
API overview
| Module | Purpose |
|---|---|
tplx |
Loader, Lookup, the engines, the Template contract and the registry |
tplx.exceptions |
TplxError and its subclasses |
tplx.engines |
the shipped engine classes |
tplx.testing |
EngineContract, the suite every engine passes |
tplx.cli |
the tplx command (cli extra) |
Development
python -m venv .venv/3.14
.venv/3.14/bin/python -m pip install -e ".[dev,docs]" # Scripts/ on Windows
.venv/3.14/bin/python -m pytest -q -rs
Releasing
This project follows Semantic Versioning and keeps a
CHANGELOG.md.
Pushing a tag matching v* triggers the release workflow: test and
dependency-floor gates → build (checking the tag names the version built) →
an installed-wheel smoke test and a strict docs build as gates → GitHub
release → publish. The release workflow never deploys the docs site
itself: for a final release its last job dispatches the docs workflow at the
tag, which owns every Pages deploy.
License
MIT — see LICENSE.
Metadata
Release files for tplx 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| tplx-0.1.0.tar.gz | 59.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| tplx-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 112.6 kB
Release files / tplx-0.1.0.tar.gz
| Download URL | tplx-0.1.0.tar.gz |
|---|---|
| Size | 59.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
012a7f5cc6ea005365d7aaf09b1ff40f98908eee31a7733e424243d4f949c3a9
|
|
BLAKE2b-256 checksum How to use checksums |
9b4997c3e463a6723312f73a08d3f7a1bf7965515c0c639c3dfeeb0d86fafb63
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.
Transparency logRelease files / tplx-0.1.0-py3-none-any.whl
| Download URL | tplx-0.1.0-py3-none-any.whl |
|---|---|
| Size | 53.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
129a45e9d1f85a655f1e812c5790be440483c4832203a6967098533181d0d819
|
|
BLAKE2b-256 checksum How to use checksums |
95d6edcc1a2df36d30c14193bc1bae7e622bb4d7226cebe0ac4bcb19813ede75
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.
Transparency log