Skip to main content

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_name must match a field on your TypedTemplate subclass.
  • TypeName uses 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

jinja_typed_template-0.6.0.tar.gz (11.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jinja_typed_template-0.6.0-py3-none-any.whl (9.3 kB view details)

Uploaded Python 3

File details

Details for the file jinja_typed_template-0.6.0.tar.gz.

File metadata

  • Download URL: jinja_typed_template-0.6.0.tar.gz
  • Upload date:
  • Size: 11.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","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

Hashes for jinja_typed_template-0.6.0.tar.gz
Algorithm Hash digest
SHA256 f6ebd52d14e1ec6b5d0393d27070b02a132c144191ca43ef2fdfcc27cf3db0fd
MD5 f579cce9a44d717cfaa96f628106d9b7
BLAKE2b-256 67e31b98c595e10b38d3ca294b891cb617881b2f9c581c379cb075761bff170b

See more details on using hashes here.

File details

Details for the file jinja_typed_template-0.6.0-py3-none-any.whl.

File metadata

  • Download URL: jinja_typed_template-0.6.0-py3-none-any.whl
  • Upload date:
  • Size: 9.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","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

Hashes for jinja_typed_template-0.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9a1bc2256c00faac7b3ae6016feef448e35af55339e9a30cdbb6af03a046473a
MD5 86185d5367b9bc3c69914a3168645223
BLAKE2b-256 9d512b41db3dc8d9be661ace6a963ae89e6c31975c754cb03cef5079b13f8a4a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page