Skip to main content

Pytest plugin to lint Jinja2 templates in FastAPI applications

Project description

pytest-jinja-check

A pytest plugin that lints Jinja2 templates used in FastAPI applications. Catches common issues at test time:

  1. Syntax validation — templates parse without errors
  2. Hardcoded route detectionhref="/foo" should be url_for('foo')
  3. Endpoint validationurl_for('endpoint') references actually exist in your app
  4. Context variable checking — routes pass all variables their templates need

Installation

pip install pytest-jinja-check

Or with uv:

uv add pytest-jinja-check

For endpoint validation against a live FastAPI app, install with the fastapi extra:

pip install "pytest-jinja-check[fastapi]"
# or
uv add "pytest-jinja-check[fastapi]"

Quick start

The plugin auto-registers with pytest via entry points. Add configuration to your pyproject.toml:

[tool.pytest-jinja-check]
template_dir = "templates"
python_dir = "app"

Then write tests using the provided fixtures:

# tests/test_templates.py

def test_template_syntax(template_syntax_errors):
    assert not template_syntax_errors, "\n".join(str(e) for e in template_syntax_errors)

def test_no_hardcoded_routes(hardcoded_routes):
    assert not hardcoded_routes, "\n".join(str(e) for e in hardcoded_routes)

def test_context_variables(missing_context_variables):
    assert not missing_context_variables, "\n".join(
        str(e) for e in missing_context_variables
    )

def test_url_for_endpoints(validate_endpoints):
    from myapp.main import app
    errors = validate_endpoints(app)
    assert not errors, "\n".join(str(e) for e in errors)

Run your tests as usual:

pytest

Fixtures

All analysis fixtures are session-scoped (run once per test session).

Fixture Returns Description
template_linter_config LinterConfig Resolved configuration
template_variables dict[str, TemplateInfo] Template name -> extracted variables (with inheritance)
route_contexts list[RouteContext] All TemplateResponse calls found via AST
template_syntax_errors list[LintError] Templates that fail to parse
hardcoded_routes list[LintError] Hardcoded URLs that should use url_for()
missing_context_variables list[LintError] Routes missing required template variables
validate_endpoints callable(app) -> list[LintError] Factory: validates url_for() refs against a live app

Configuration

All settings in [tool.pytest-jinja-check] in your pyproject.toml:

Key Default Description
template_dir "templates" Template directory relative to project root
python_dir "." Directory to scan for Python route files
route_file_patterns ["**/*.py"] Glob patterns for route files
ignore_variables ["request", "url_for", ...] Variables to skip (framework-provided)
allowed_url_prefixes ["#", "http://", ...] URL prefixes that aren't hardcoded routes

You can also pass --template-lint-config <path> to pytest to specify a different directory containing pyproject.toml.

Programmatic API

The analysis functions can also be used outside of pytest:

from pytest_jinja_check import (
    analyze_all_templates,
    check_syntax,
    check_hardcoded_routes,
    check_context_variables,
    validate_url_for_references,
    extract_all_route_contexts,
)
from pathlib import Path

# Analyze templates
templates = analyze_all_templates(Path("templates"))
for name, info in templates.items():
    print(f"{name}: needs {info.variables}")

# Find issues
syntax_errors = check_syntax(Path("templates"))
hardcoded = check_hardcoded_routes(Path("templates"))
routes = extract_all_route_contexts(Path("app"))
missing = check_context_variables(Path("templates"), routes)

How it works

  • Template analysis uses jinja2.meta.find_undeclared_variables() on parsed ASTs, recursively resolving {% extends %} to capture inherited variable requirements.
  • Route analysis uses Python's ast module to find TemplateResponse(...) calls and extract template names and context dict keys. Handles both the old positional API and newer keyword API.
  • Endpoint validation introspects a live FastAPI app.routes to get registered endpoint names.

Known limitations

  • Template variables from dynamic {% extends variable %} can't be resolved.
  • Context dicts built outside the TemplateResponse(...) call (e.g., ctx = {...}; TemplateResponse("t.html", ctx)) are flagged as dynamic and skipped rather than producing false positives.
  • The variable union from template inheritance may over-report when a child block completely replaces a parent block that used a variable.

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

pytest_jinja_check-1.0.1.tar.gz (47.8 kB view details)

Uploaded Source

Built Distribution

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

pytest_jinja_check-1.0.1-py3-none-any.whl (14.0 kB view details)

Uploaded Python 3

File details

Details for the file pytest_jinja_check-1.0.1.tar.gz.

File metadata

  • Download URL: pytest_jinja_check-1.0.1.tar.gz
  • Upload date:
  • Size: 47.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pytest_jinja_check-1.0.1.tar.gz
Algorithm Hash digest
SHA256 ae8989a5c11b6ff8cf19b389255bc4e6cb2be2bada4271c0125b191d3acb85b8
MD5 8050fbc45a4559ce9d3c8af567f20407
BLAKE2b-256 e723f67f21d497067c91801dc9484bb979ed0dd7a9a7b795002e42395d7c5add

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_jinja_check-1.0.1.tar.gz:

Publisher: release.yml on Promptly-Technologies-LLC/pytest-jinja-check

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pytest_jinja_check-1.0.1-py3-none-any.whl.

File metadata

File hashes

Hashes for pytest_jinja_check-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 501dd23c3ccdbbabfc6ee9219539f8a84b940d7f38e8430c2bbefa1fce6f6941
MD5 d05bcfcd3888d204e9143da8a15c2a00
BLAKE2b-256 2312f7c3f84698144f358de8852667c112b1d91566c7b6ba28b8c76c2abff88c

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_jinja_check-1.0.1-py3-none-any.whl:

Publisher: release.yml on Promptly-Technologies-LLC/pytest-jinja-check

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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