jinja-typed-template
Type-safe Jinja2 templates with compile-time validation — keep your Python code and Jinja templates in sync, fail fast on mismatches, and never ship a broken template again.
Installation
pip install jinja-typed-template
For Flask support:
pip install jinja-typed-template[flask]
Quick Start
1. Annotate your template
Add a header comment declaring every variable the template uses, along with its type:
{#
name: str
age: int
items: list[str]
#}
<h1>Hello {{ name }}!</h1>
<p>You are {{ age }} years old.</p>
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
2. Define a typed template class
Subclass TypedTemplate, set the template name, and declare fields with type hints:
from jinja_typed_template import TypedTemplate, env_context
from jinja2 import Environment, FileSystemLoader
class ProfileTemplate(TypedTemplate):
__template_name__ = "profile.html"
name: str
age: int
items: list[str]
3. Render safely
Wrap usage in env_context to bind a Jinja2 environment, then instantiate and render:
env = Environment(loader=FileSystemLoader("templates"))
with env_context(env):
t = ProfileTemplate(name="Alice", age=30, items=["apples", "bananas"])
print(t.render())
4. Flask setup (optional)
If you're using Flask, initialize the extension on your app — then TypedTemplate works in any route without manual env_context:
from flask import Flask
from jinja_typed_template.flask import TypedTemplateExtension
app = Flask(__name__)
typed_templates = TypedTemplateExtension()
typed_templates.init_app(app)
Now use TypedTemplate directly in a route:
from jinja_typed_template import TypedTemplate
class ProfileTemplate(TypedTemplate):
__template_name__ = "profile.html"
name: str
age: int
@app.route("/profile/<name>")
def profile(name: str):
t = ProfileTemplate(name=name, age=30)
return t.render()
What Gets Validated
Every TypedTemplate instance is validated at construction time (__post_init__). Three checks run automatically:
| Check | Catches |
|---|---|
| Type checking | T(name=123) when name: str — raises TypeError |
| Variable matching | Missing or extra fields vs. what the template actually uses — raises ValueError |
| Header comment consistency | Header declares name: int but class says name: str — raises TypeError |
This means you catch mismatches the moment you create the object, not when a user hits the page.
API Reference
TypedTemplate
Base class. Subclass it, set __template_name__, and declare typed fields.
| Member | Description |
|---|---|
render() |
Render the template to a string |
context_dict |
Dict of all field names → values |
copy_updating(**kwargs) |
Return a new instance with some fields replaced (instances are frozen/immutable) |
env_context(env)
Context manager that binds a Jinja2 Environment for the current thread/context. Required before creating or rendering any TypedTemplate.
from jinja_typed_template import env_context
with env_context(env):
t = MyTemplate(...)
TypedTemplateExtension (Flask)
See the Flask setup in Quick Start above.
Header Comment Format
Place a Jinja2 comment at the very top of the template:
{#
variable_name: TypeName
another_var: list[str]
optional_var: str = "default"
#}
variable_namemust match a field on yourTypedTemplatesubclass.TypeNameuses a short-form representation (str,int,list[str],dict[str, int], etc.) and must match the Python type hint.- Default values after
=are stripped — they're documentation only.
License
MIT
Metadata
Release files for jinja-typed-template 0.6.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| jinja_typed_template-0.6.1.tar.gz | 7.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| jinja_typed_template-0.6.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 17.1 kB
Release files / jinja_typed_template-0.6.1.tar.gz
| Download URL | jinja_typed_template-0.6.1.tar.gz |
|---|---|
| Size | 7.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d3c3e783f782cd0bb75af1894d79e1e6ed081ff0d3f8891afae3ba36b5b61743
|
|
BLAKE2b-256 checksum How to use checksums |
dbc863a151f4f757234889314a21647c83fc76806a30c29668888ff4890a3e3a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / jinja_typed_template-0.6.1-py3-none-any.whl
| Download URL | jinja_typed_template-0.6.1-py3-none-any.whl |
|---|---|
| Size | 9.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
843cdf32bc3cbd96573e26b725c5085457c81805eab381bc0ead68f439eafbb0
|
|
BLAKE2b-256 checksum How to use checksums |
e086f5bc38dad1020d80bfedf6f576c1b4e1eb547f77880f235889a64bbf42f9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|