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.3.0.tar.gz (11.0 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.3.0-py3-none-any.whl (8.9 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: jinja_typed_template-0.3.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

Hashes for jinja_typed_template-0.3.0.tar.gz
Algorithm Hash digest
SHA256 0795dcc5e4e23d89b1495d191d53f41613a03380a8a08c0b97a9b2d0fdd7a7af
MD5 7bfcfb466d7837294ecb1dde2a414013
BLAKE2b-256 5df3af516604ecfc143c37b2a97e0b165c0a92e2392bc57ba5218065b87a5627

See more details on using hashes here.

File details

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

File metadata

  • Download URL: jinja_typed_template-0.3.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

Hashes for jinja_typed_template-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 bd07803d94db08ca08ec33c83c8ddfb99577aaab529fbf8e8aa7ec80efa47610
MD5 b4e218d2ccb0143dc5752969cbd7b2d3
BLAKE2b-256 750b4d945e020434fe61afd73f8714c73ddba605d1a09b1c7fe3dde6fa97d807

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