'After rendering a Jinja2 template, can I get the set values?

I want to render a Jinja2 template, and after the rendering, read the values that were set by it:

import jinja2

template = jinja2.Template("""
    {% set number = 42 %}
    Hello {{name}} {{number}}!
    """)

vars = {"name": "Ned"}
print(template.render(vars).strip())
print(vars)

This prints:

Hello Ned 42!
{'name': 'Ned'}

Is there something I can do after template.render that would give me the value of number? Also, I need to do it without knowing the name number beforehand. I want to discover whatever values have been set in the template.



Solution 1:[1]

Here's a way to do it without using functions that are documented, but say not to use them:

import jinja2

template = jinja2.Template("""
    {% set number = 52 %}
    {% set choice = "apple" %}
    Hello {{name}} {{number}}!
    {% if number > 50 %}
        More than 40!
        {% set also = 'foo' %}
    {% endif %}
    """)

ctx = template.new_context(vars = {"name": "Ned"})
template.render(ctx)
mod = template.module
template_vars = {n:getattr(mod, n) for n in dir(mod) if not n.startswith("_")}
print(template_vars)

This prints:

{'also': 'foo', 'choice': 'apple', 'number': 52}

Solution 2:[2]

With the context provided:

import jinja2
import re

template = jinja2.Template("""
    {% set number = 42 %}
    Hello {{name}} {{number}}!
    """)

vars = {"name": "Ned"}
rendered = template.render(vars).strip()
print(vars)
print(rendered)
changed_var_value = re.findall(r'Hello[\s\S]*\s{1}(.*)!', rendered)
print(f'var after the greeting: {changed_var_value}')

output:

{'name': 'Ned'}
Hello Ned 42!
var after the greeting: ['42']

Solution 3:[3]

I guess, this is a kinda hack but it seems you can do something with Context from jinja2.runtime like

$ cat show_vars.py
import jinja2

template = jinja2.Template("""
    {% set number = 42 %}
    Hello {{name}} {{number}}!
    """)

vars = {"name": "Ned"}
print(template.render(vars).strip())
print(vars)

from jinja2 import Environment
from jinja2.runtime import Context

env = Environment()

ctx = Context(env, vars, '', template.blocks)
list(template.root_render_func(ctx))
print(ctx.vars)
print(ctx.get_all())

$ python show_vars.py
Hello Ned 42!
{'name': 'Ned'}
{'number': 42}
{'name': 'Ned', 'number': 42}

As @nedbat pointed out, one can also achieve the same with

import jinja2

template = jinja2.Template("""
    {% set number = 42 %}
    Hello {{name}} {{number}}!
    {% if number > 50 %}
        More than 40!
        {% set also = 'foo' %}
    {% endif %}
    """)

ctx = template.new_context(vars = {"name": "Ned"})
list(template.root_render_func(ctx))
print("get_all:", ctx.get_all())
print("exported_vars:", ctx.exported_vars)
print("vars:", ctx.vars)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Ned Batchelder
Solution 2 jeroenflvr
Solution 3