Это то, что я придумал, используя опцию finalize для Environment:
def render (tpl, args):
scope = {}
scope['env'] = jinja2.Environment (finalize=lambda x: scope['env'].from_string (x).render (**args) if isinstance(x, str) and '{{' in x else x)
scope['env'].filters['q'] = lambda value: re.sub (r'"', r'\\"', value)
return scope['env'].from_string (tpl).render (**args)
Это немного уродливо, поэтому улучшения приветствуются:)
Но он выполняет рекурсивный рендеринг, как запрашивал OP, и не останавливается до тех пор, пока на выходе не останется {{
Пример сеанса
>>> import jinja2
>>> def render (tpl, args):
... scope = {}
... scope['env'] = jinja2.Environment (finalize=lambda x: scope['env'].from_string (x).render (**args) if isinstance(x, str) and '{{' in x else x)
... scope['env'].filters['q'] = lambda value: re.sub (r'"', r'\\"', value)
... return scope['env'].from_string (tpl).render (**args)
...
>>> render("this {{ outer_var }} wokring!", {"outer_var":"{{ inner_var }}", "inner_var":"{{ final_step }}", "final_step":"is"})
u'this is wokring!'
EDIT:
Хорошо, немного изменил мою функцию рендеринга, функционально то же самое, но немного более аккуратно:
def render (tpl, args):
@jinja2.environmentfunction
def finalize (env, value):
if isinstance(value, (str, unicode)) and '{{' in value:
return env.from_string (value).render (args)
return value
env = jinja2.Environment (finalize=finalize)
env.filters['q'] = lambda value: re.sub (r'"', r'\\"', value)
return env.from_string (tpl).render (args)