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.4.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.4.0-py3-none-any.whl (8.9 kB view details)

Uploaded Python 3

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

Hashes for jinja_typed_template-0.4.0.tar.gz
Algorithm Hash digest
SHA256 b27765e6c619ad045ebce7145c13097d46db28576fd54a1fa016f90efe897949
MD5 c8a9345b6ee187ea4746113c334476fb
BLAKE2b-256 6c2209ba91e2f13a43a1cf2353f9b1b2d1ba4ec1a6988a15169bb01eda82e60c

See more details on using hashes here.

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

Hashes for jinja_typed_template-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0ba18d66b6da5549b068a419ef1bd660194c399e2d6dfe5e859babeba9c99fef
MD5 02d98d10421dcc6b471d303215be65ec
BLAKE2b-256 b1bc436b66bba64cf072bf3c273f45dc56b7fc9bd50641248180da37078293c5

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