Stencils — Lightweight Template Loader for Python
Overview
Stencils is a minimal, zero-dependency Python library for managing text templates embedded in files.
It’s ideal for projects that need small, human-readable, and reusable template snippets (e.g., config fragments, codegen templates, or documentation blocks).
The library parses .tmpl (or any text) files containing named template blocks, resolves duplicates, and provides a clean dictionary-like interface for use in your code.
Installation
pip install stencils
Template Format
A single file can contain multiple named template blocks.
Each block must start with a line of the form:
===== TEMPLATE:<name> =====
and must end with:
===== /TEMPLATE =====
Example:
===== TEMPLATE:greeting =====
Hello {name}!
Welcome to {place}.
===== /TEMPLATE =====
===== TEMPLATE:farewell =====
Goodbye {name}, see you soon!
===== /TEMPLATE =====
Usage
Loading Templates
from stencils import load, render
templates = load("templates/example.tmpl")
print(templates["greeting"])
# Output:
# Hello {name}!
# Welcome to {place}.
You can also load multiple files, directories, or patterns:
templates = load(["./templates", "common/*.tmpl"])
Each
.tmplfile is scanned forTEMPLATE:blocks.
Duplicate keys across files will raiseTemplateConflictErrorunless the templates are identical.
Rendering Templates
You can render any loaded template with a dictionary of values:
text = render(templates["greeting"], {"name": "Alice", "place": "Wonderland"})
print(text)
Output:
Hello Alice!
Welcome to Wonderland.
Missing placeholders default to empty strings, so this is safe:
render(templates["farewell"], {})
# -> "Goodbye , see you soon!"
Controlling Missing or Invalid Placeholders
You can control how missing and invalid placeholders are handled via on_missing and on_error options.
render(template, data, on_missing="empty", on_error="keep")
Available options:
| Parameter | Option | Description | Example Output |
|---|---|---|---|
on_missing |
"empty" (default) |
Replace missing keys with an empty string. | "Hello , welcome!" |
"keep" |
Keep the original placeholder text {key} if it’s missing. |
"Hello {name}, welcome!" |
|
"error" |
Raise a KeyError for missing placeholders. |
Raises KeyError: 'name' |
|
on_error |
"keep" (default) |
Keep the original placeholder if formatting fails (e.g., bad spec). | "Value: {price:.2f}" |
"empty" |
Replace invalid placeholders with an empty string. | "Value: " |
|
"error" |
Raise the formatting exception immediately. | Raises ValueError or similar |
Foreign Placeholders (${...})
Some templates may include foreign placeholders (like shell or CI variables) using ${...} syntax.
These are not interpreted by Stencils and are automatically escaped so they remain intact.
tpl = "User: {name}, shell: ${uname -u}, gha: ${{ matrix.os }}"
render(tpl, {"name": "Io"})
# -> "User: Io, shell: ${uname -u}, gha: ${{ matrix.os }}"
You can disable this behavior with protect_foreign=False if you want raw handling.
Errors
| Exception | Description |
|---|---|
TemplateConflictError |
Raised when a template key appears in multiple files with different content. |
TemplateParseError |
Raised on malformed templates (e.g., unclosed or nested blocks, missing start/end). |
API Reference
load(targets)
Load one or more template sources (files, directories, or globs).
Args:
targets: Path, string, or iterable of paths/globs.
Returns:
A dict[str, str] mapping template names to their contents.
Each template is also available under an alias prefixed with a colon (:{name}).
render(template, data)
Render a template using Python’s str.format_map, defaulting missing keys to empty strings.
Args:
template: Template string.data: Dict of substitutions.
Returns: Rendered string.
Example Project Structure
myproject/
├── templates/
│ ├── main.tmpl
│ └── footer.tmpl
└── app.py
# app.py
from stencils import load, render
templates = load("templates")
output = render(templates["main"], {"title": "Hello World"})
print(output)
🧾 License
This project is licensed under the MIT License — see the LICENSE file for details.
© 2025 Ioannis D. (devcoons)
Metadata
Release files for stencils 0.1.8
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| stencils-0.1.8.tar.gz | 6.6 kB | Without provenance | Details for stencils-0.1.8.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| stencils-0.1.8-py3-none-any.whl | Python 3 | none | any | Details for stencils-0.1.8-py3-none-any.whl |
Total release size: 14.1 kB
Release files / stencils-0.1.8.tar.gz
| Download URL | stencils-0.1.8.tar.gz |
|---|---|
| Size | 6.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3394fc3771530eddadfa61802727c57454e75c42f6698ae5591c7a6be6a83e72
|
|
BLAKE2b-256 checksum How to use checksums |
9d35b0ccb1f5129ff043bd0062c48c9b4bbb793ec2e5a1e9813121b4367a7ca8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.23
|
Release files / stencils-0.1.8-py3-none-any.whl
| Download URL | stencils-0.1.8-py3-none-any.whl |
|---|---|
| Size | 7.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4cb206a57fad9b964ef8251e6541549cb8eb76c50ba449e3cde0185f0763a93a
|
|
BLAKE2b-256 checksum How to use checksums |
5e709af14837aced3440030db771bcb37460f9633ea939db33e0320c4bf540cb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.23
|
Security for stencils 0.1.8
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.