I have a Django SaaS app with users who create their own Jinja2 templates (in an extraordinarily sandboxed environment, for those who just cringed), which are saved to a database. I have a template_type
field, noting whether or not a given template is an "include" or a "full template" (a full template can of course include "includes"). The problem is that a user could put {% include "foo" %}
into a template called "bar"
, and {% include "bar" %}
into the "foo"
template, causing a RuntimeError: maximum recursion depth exceeded
type of situation, which would not be good for performance.
Is there a nice way to handle this situation that doesn't include a validation regexp (e.g., r'\{%\s+include'
) check for includes during user template creation time ("Make sure a recursive import never gets in the database, or your server will asplode" doesn't quite jive with me).
My failed attempt
I started by using a custom loader which only contains a user's "includes"::
def get_source(self, environment, template):
"""
Returns the source of the template or raises ``TemplateNotFound``.
This loader is for use with an environment which intends to load each
template from a string, since only includes are loaded from here.
"""
try:
template = self.snippets[template]
except KeyError:
raise jinja2.TemplateNotFound(
template, message=u'Snippet "{s}" was not found'.format(s=template)
)
else:
source = template['source']
return (source, None, lambda: True)
The problem with doing this is that I've basically blocked myself from being able to take advantage of Jinja2's Bytecode Cache, which would clearly require that all the templates be available for a load(...
call, which in turn calls get_source(...
.