Skip to main content

Track which templates are used by Jinja2

  • Posted

Override the get_template method on the Environment and see which templates are summoned.

I write my own static site generator, and I do incremental rebuilds when a source file changes. Currently I rebuild the entire website whenever a template changes, which is a lot of redundant work.

I thought it might be useful to track which templates are used by each page, and only rebuild a page if it uses the changed template. I use Jinja for templating, and I wrote this script to work out where I should intercept template calls:

from jinja2 import DictLoader, Environment


class PrintingDictLoader(DictLoader):
    def get_source(self, env: Environment, template: str):
        print(f"Loader.get_source({template!r})")
        return super().get_source(env, template)


class PrintingEnvironment(Environment):
    def get_template(self, name: str, *args, **kwargs):
        print(f"Environment.get_template({name!r})")
        return super().get_template(name, *args, **kwargs)


if __name__ == "__main__":
    loader = PrintingDictLoader(
        {
            "base.html": (
                "This is the base template"
                "{% block content %}{% endblock %}"
            ),
            "article.html": (
                '{% extends "base.html" %}'
                "{% block content %}"
                "This is article {{ title }}"
                "{% endblock %}"
            ),
        }
    )
    env = PrintingEnvironment(loader=loader)

    env.get_template("article.html").render(title="My first article")
    print("---")
    env.get_template("article.html").render(title="My second article")

Here’s the output. We can see the get_template method is called on both renders, but the loader method is only called once because the loaded template gets cached:

$ python3 print_templates.py
Environment.get_template('article.html')
Loader.get_source('article.html')
Environment.get_template('base.html')
Loader.get_source('base.html')
---
Environment.get_template('article.html')
Environment.get_template('base.html')

I’m not pursuing this for now, because my template code is complicated enough already and I need to simplify it a bit before adding more complexity. (Also, I spend less and less time editing templates, so I don’t feel the effect as much.)

I’m writing this note in case I revisit this idea later.