Skip to main content

tplx

Version Python versions License: MIT Docs CI

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, Go's text/template, ERB, EPP, envsubst (as GNU, a8m or drone) 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.cfg before boot.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 DataEncoder subclass 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.engines entry point, and checked by the shipped tplx.testing.EngineContract suite.
  • 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}}
Go (text/template) .tmpl .gotmpl tplx[go], Python 3.11+ strict; otherwise <no value> {{template "name" .}}
ERB .erb ruby on PATH Ruby's nil —
EPP .epp puppet on PATH Puppet's undef —
shell (%{NAME}) .shtpl — strict, lenient, debug —
envsubst ($NAME, GNU, a8m or drone) .envsubst .template — 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 &amp;), 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
tplx render nginx.conf -r templates --env -u debug       # envsubst from the environment

-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.2

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

Source distribution (sdist)

Source distribution for tplx 0.1.2
File Size Uploaded
tplx-0.1.2.tar.gz 71.0 kB Details

Built distribution (wheel)

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

Total release size: 132.2 kB

Release files / tplx-0.1.2.tar.gz

Download URL tplx-0.1.2.tar.gz
Size 71.0 kB
Tags Source
SHA-256 checksum
How to use checksums
1fa81fddaa54e9ba4ecb0ea0688c722a4c8a8da84b7ea25c977cc2ec740f4370
BLAKE2b-256 checksum
How to use checksums
541bda2ff0d0e14cda2f1af1cf5d75be093f13162c46be7ce4c0cdbcc1f78c0a
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 15, 2026.

Transparency log

Release files / tplx-0.1.2-py3-none-any.whl

Download URL tplx-0.1.2-py3-none-any.whl
Size 61.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
713b537adb76521413844998933470e259c24bf803f04d5b4825f0916750a33d
BLAKE2b-256 checksum
How to use checksums
3ff6e7c75a10aa3d815cc30c14ba320f2f3ce72e6eccfc1627ca42b0440e7455
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 15, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.2 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

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