is there a way to render the variable b-c
(456) by changing only template
variable in the following code?
import jinja2
environment = jinja2.Environment()
data = {'a':123, 'b-c':456}
template = "hello {{ b-c }}"
result = environment.from_string(template, globals = data).render()
print('result:', result)
the expected result:
hello 456
current code throws error:
jinja2.exceptions.UndefinedError: 'b' is undefined
i can't change how template variables (data) defined or how template is rendered at the moment. but i can change template.
b-c
is not a valid identifier, so you would need to hack into Jinja2's internals to retrieve its value.
This can be done by identifying a Python-implemented class (as in a class that isn't implemented in C) so we can obtain the __builtins__
attribute of one of its methods and access the locals
function from it.
One such class in the Jinja2 environment is jinja2.runtime.Undefined
, which is instantiated when an undefined name is evaluated. We'll use the name _
for the demo here. This object has an __init__
method which you can use to obtain the __builtins__
attribute.
Once you obtain the local namespace through the built-in locals
function, you can obtain the jinja2.runtime.Context
object of the frame as the __self
variable, though you would have to look it up by the name of _Context__self
because it is prefixed with double underscores and is therefore subject to name mangling.
The Context
object supports name lookups through the __getitem__
method so you can then look up the key 'b-c'
with square brackets:
import jinja2
environment = jinja2.Environment()
data = {'a':123, 'b-c':456}
template = "hello {{ _.__init__.__builtins__.locals()._Context__self['b-c'] }}"
result = environment.from_string(template, globals = data).render()
print('result:', result)
This outputs:
result: hello 456
Demo here