55 lines
1.6 KiB
Python
55 lines
1.6 KiB
Python
import json
|
|
|
|
import jinja2
|
|
|
|
|
|
# The Jinja2 environment
|
|
__ENV = None
|
|
|
|
|
|
def _env_init(force: bool = False):
|
|
"""
|
|
Initialize the Jinja2 environment.
|
|
:param force: If true, force reload the environment.
|
|
"""
|
|
global __ENV
|
|
if __ENV is None or force:
|
|
# Use json.dumps as finalizer in order to preserve complex data structures
|
|
__ENV = jinja2.Environment(finalize=json.dumps)
|
|
return __ENV
|
|
|
|
|
|
def render(template: str):
|
|
"""
|
|
Render the given string as a Jinja2 template.
|
|
:param template: The template string to render.
|
|
"""
|
|
# Make sure the Jinja2 environment is initialized
|
|
env = _env_init()
|
|
# Create a Jinja2 template from the input string
|
|
t = env.from_string(template)
|
|
decoder = json.JSONDecoder()
|
|
# Render the template and turn the JSON dump back into complex data structures
|
|
# Only parse the first JSON object in the string, ignore the rest
|
|
obj, i = decoder.raw_decode(t.render())
|
|
return obj
|
|
|
|
|
|
def render_traverse(obj):
|
|
"""
|
|
Walk through a complex, JSON-serializable data structure, and pass
|
|
string objects through the Jinja2 templating engine.
|
|
:param obj: The object to traverse.
|
|
"""
|
|
if isinstance(obj, list):
|
|
# list -> recurse into each item
|
|
return [render_traverse(x) for x in obj]
|
|
elif isinstance(obj, dict):
|
|
# dict -> recurse into the value of each (key, value)
|
|
return {k: render_traverse(v) for k, v in obj.items()}
|
|
elif isinstance(obj, str):
|
|
# str -> template
|
|
return render(obj)
|
|
else:
|
|
# anything else -> return as-is
|
|
return obj
|