pallets/jinja

Template globals not visible in {% import %}'ed file when it's {% include %}'ed by another file

clarkwang opened this issue · 2 comments

I've come up with the following minimal repro steps.

Python script:

$ cat test.py
#!/usr/bin/env python3

import jinja2 as j2
import os, sys

print(f'jinja2 version: {j2.__version__}')

env = j2.Environment(loader=j2.FileSystemLoader('.') )
tmpl = env.get_template(sys.argv[1], globals={ 'ENV': os.getenv })
print(tmpl.render() )

Data files:

$ cat common
{% set var1 = ENV('HOME') %}
$ cat file1
{% include 'file2' %}
$ cat file2
{% import 'common' as COMMON %}
{{ COMMON.var1 }}

To repro the issue:

$ python3 test.py file2
jinja2 version: 3.1.3

/root
$ python3 test.py file1
jinja2 version: 3.1.3
Traceback (most recent call last):
  File "/root/void/python/jinja/t1/test.py", line 10, in <module>
    print(tmpl.render() )
          ^^^^^^^^^^^^^
  File "/usr/local/py3/lib/python3.11/site-packages/jinja2/environment.py", line 1301, in render
    self.environment.handle_exception()
  File "/usr/local/py3/lib/python3.11/site-packages/jinja2/environment.py", line 936, in handle_exception
    raise rewrite_traceback_stack(source=source)
  File "file1", line 1, in top-level template code
    {% include 'file2' %}
    ^^^^^^^^^^^^^^^^^^^^^^
  File "file2", line 1, in top-level template code
    {% import 'common' as COMMON %}
    ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/py3/lib/python3.11/site-packages/jinja2/environment.py", line 1405, in make_module
    return TemplateModule(self, ctx)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/py3/lib/python3.11/site-packages/jinja2/environment.py", line 1535, in __init__
    body_stream = list(template.root_render_func(context))
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "common", line 1, in top-level template code
    {% set var1 = ENV('HOME') %}
    ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/py3/lib/python3.11/site-packages/jinja2/utils.py", line 83, in from_obj
    if hasattr(obj, "jinja_pass_arg"):
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
jinja2.exceptions.UndefinedError: 'ENV' is undefined

As we can see, python3 test.py file2 works but python3 test.py file1 fails.

j7an commented

@clarkwang The error occurs because globals is being incorrectly passed to get_template. The correct way to add globals to the Jinja2 environment is to set them on the Environment object itself.

#!/usr/bin/env python3

import jinja2 as j2
import os
import sys

print(f'jinja2 version: {j2.__version__}')

# Set up Jinja2 environment and add the global function
env = j2.Environment(loader=j2.FileSystemLoader('.'))
env.globals['ENV'] = os.getenv

# Load the template specified by the first command line argument
tmpl = env.get_template(sys.argv[1])

# Render the template
print(tmpl.render())
  • get_env_variable Function: You use the os.getenv directly without wrapping it in another function. This makes the template code {% set var1 = ENV('HOME') %} work correctly, as ENV now maps directly to os.getenv.
  • Setting Global in Environment: By setting env.globals['ENV'] = os.getenv, the environment variable access function is globally available in all templates rendered by this environment.

@j7an adding globals to a template is supported: https://jinja.palletsprojects.com/en/3.1.x/api/#the-global-namespace. That said, you're correct that they're not setting the globals in the correct place. In their example, they set the globals on file1, but it's common that needs access. They should either set the globals on common, or on the environment as you showed.