Static type checking for Jinja templates
Project description
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
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file jinja_typed_template-0.4.0.tar.gz.
File metadata
- Download URL: jinja_typed_template-0.4.0.tar.gz
- Upload date:
- Size: 11.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b27765e6c619ad045ebce7145c13097d46db28576fd54a1fa016f90efe897949
|
|
| MD5 |
c8a9345b6ee187ea4746113c334476fb
|
|
| BLAKE2b-256 |
6c2209ba91e2f13a43a1cf2353f9b1b2d1ba4ec1a6988a15169bb01eda82e60c
|
File details
Details for the file jinja_typed_template-0.4.0-py3-none-any.whl.
File metadata
- Download URL: jinja_typed_template-0.4.0-py3-none-any.whl
- Upload date:
- Size: 8.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0ba18d66b6da5549b068a419ef1bd660194c399e2d6dfe5e859babeba9c99fef
|
|
| MD5 |
02d98d10421dcc6b471d303215be65ec
|
|
| BLAKE2b-256 |
b1bc436b66bba64cf072bf3c273f45dc56b7fc9bd50641248180da37078293c5
|