Opened 10 years ago

Closed 9 years ago

Last modified 9 years ago

#23441 closed Bug (fixed)

Maximum recursion depth exceeded when using custom `inclusion_tag` with recursion in templates and `django.template.loaders.cached.Loader`

Reported by: Venelin Stoykov Owned by: nobody
Component: Template system Version: 1.7
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: yes Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Venelin Stoykov)

When custom inclusion_tag is created which use recursion in the template (call itself) and also template loader used is django.template.loaders.cached.Loader. In this situation when template containing this inclusion tag is rendered for the second time (then the template is fetched from the cache) then maximum recursion depth exceeded error is raised.

The reason for this is when {% extends %} template tag start to look for {% block %} tags it calls Node.get_nodes_by_type which start looking for {% block %} tags in the template. When reach our custom inclusion tag node and call it's get_nodes_by_type method to look for {% block %} then start iteration over cached nodes from included template. This will not happen when standard template loaders are used because template for inclusion tag is not parsed yet.

To reproduce the error you can use github repo created for that purpose (https://github.com/vstoykov/wpadmin-test)

I think that there is two possible approaches to fix this, both of which prevent iteration over cached nodes.

  1. Set child_nodelists attribute of InclusionNode to be empty iterable (tuple for example).
  2. Change the name of the attribute in which the cached nodes are stored from nodelist to something else.

For me the first variant is preferred because we explicitly tell that this node doesn't have any child nodes.

I created a patch with first variant and also a pull request.

Attached Traceback:

Environment:

Request Method: GET
Request URL: http://localhost:8000/admin/

Django Version: 1.7
Python Version: 2.7.6
Installed Applications:
('wpadmin',
 'django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'django.contrib.redirects',
 'django.contrib.sites')
Installed Middleware:
('django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware')


Template error:
In template /home/venko/.pyvirtualenvs/django-max-recursion/local/lib/python2.7/site-packages/wpadmin/templates/admin/base_site.html, error at line 1
   maximum recursion depth exceeded
   1 :  {% extends "admin/base.html" %} 
   2 : {% load i18n wpadmin_tags %}
   3 : 
   4 : {% block title %}{{ title }} | {% wpadmin_render_custom_title %}{% endblock %}
   5 : 
   6 : {% block branding %}
   7 : <h1 id="site-name">{% trans 'Django administration' %}</h1>
   8 : {% endblock %}
   9 : 
   10 : {% block nav-global %}{% endblock %}
   11 : 

Traceback:
File "/home/venko/.pyvirtualenvs/django-max-recursion/local/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  137.                 response = response.render()
File "/home/venko/.pyvirtualenvs/django-max-recursion/local/lib/python2.7/site-packages/django/template/response.py" in render
  103.             self.content = self.rendered_content
File "/home/venko/.pyvirtualenvs/django-max-recursion/local/lib/python2.7/site-packages/django/template/response.py" in rendered_content
  80.         content = template.render(context)
File "/home/venko/.pyvirtualenvs/django-max-recursion/local/lib/python2.7/site-packages/django/template/base.py" in render
  148.             return self._render(context)
File "/home/venko/.pyvirtualenvs/django-max-recursion/local/lib/python2.7/site-packages/django/template/base.py" in _render
  142.         return self.nodelist.render(context)
File "/home/venko/.pyvirtualenvs/django-max-recursion/local/lib/python2.7/site-packages/django/template/base.py" in render
  844.                 bit = self.render_node(node, context)
File "/home/venko/.pyvirtualenvs/django-max-recursion/local/lib/python2.7/site-packages/django/template/debug.py" in render_node
  80.             return node.render(context)
File "/home/venko/.pyvirtualenvs/django-max-recursion/local/lib/python2.7/site-packages/django/template/loader_tags.py" in render
  126.         return compiled_parent._render(context)
File "/home/venko/.pyvirtualenvs/django-max-recursion/local/lib/python2.7/site-packages/django/template/base.py" in _render
  142.         return self.nodelist.render(context)
File "/home/venko/.pyvirtualenvs/django-max-recursion/local/lib/python2.7/site-packages/django/template/base.py" in render
  844.                 bit = self.render_node(node, context)
File "/home/venko/.pyvirtualenvs/django-max-recursion/local/lib/python2.7/site-packages/django/template/debug.py" in render_node
  80.             return node.render(context)
File "/home/venko/.pyvirtualenvs/django-max-recursion/local/lib/python2.7/site-packages/django/template/loader_tags.py" in render
  120.                                   compiled_parent.nodelist.get_nodes_by_type(BlockNode))
File "/home/venko/.pyvirtualenvs/django-max-recursion/local/lib/python2.7/site-packages/django/template/base.py" in get_nodes_by_type
  854.             nodes.extend(node.get_nodes_by_type(nodetype))
File "/home/venko/.pyvirtualenvs/django-max-recursion/local/lib/python2.7/site-packages/django/template/base.py" in get_nodes_by_type
  831.                 nodes.extend(nodelist.get_nodes_by_type(nodetype))
File "/home/venko/.pyvirtualenvs/django-max-recursion/local/lib/python2.7/site-packages/django/template/base.py" in get_nodes_by_type
  854.             nodes.extend(node.get_nodes_by_type(nodetype))
File "/home/venko/.pyvirtualenvs/django-max-recursion/local/lib/python2.7/site-packages/django/template/base.py" in get_nodes_by_type
  831.                 nodes.extend(nodelist.get_nodes_by_type(nodetype))
File "/home/venko/.pyvirtualenvs/django-max-recursion/local/lib/python2.7/site-packages/django/template/base.py" in get_nodes_by_type
  854.             nodes.extend(node.get_nodes_by_type(nodetype))
File "/home/venko/.pyvirtualenvs/django-max-recursion/local/lib/python2.7/site-packages/django/template/base.py" in get_nodes_by_type
  831.                 nodes.extend(nodelist.get_nodes_by_type(nodetype))
File "/home/venko/.pyvirtualenvs/django-max-recursion/local/lib/python2.7/site-packages/django/template/base.py" in get_nodes_by_type
  854.             nodes.extend(node.get_nodes_by_type(nodetype))

............................

File "/home/venko/.pyvirtualenvs/django-max-recursion/local/lib/python2.7/site-packages/django/template/base.py" in get_nodes_by_type
  831.                 nodes.extend(nodelist.get_nodes_by_type(nodetype))
File "/home/venko/.pyvirtualenvs/django-max-recursion/local/lib/python2.7/site-packages/django/template/base.py" in get_nodes_by_type
  854.             nodes.extend(node.get_nodes_by_type(nodetype))
File "/home/venko/.pyvirtualenvs/django-max-recursion/local/lib/python2.7/site-packages/django/template/base.py" in get_nodes_by_type
  829.             nodelist = getattr(self, attr, None)
File "/home/venko/.pyvirtualenvs/django-max-recursion/local/lib/python2.7/site-packages/django/template/defaulttags.py" in nodelist
  298.         return NodeList(node for _, nodelist in self.conditions_nodelists for node in nodelist)

Exception Type: RuntimeError at /admin/
Exception Value: maximum recursion depth exceeded

Attachments (1)

0001-Prevent-maximumrecursiondepthexceededintemplates.patch (1.1 KB ) - added by Venelin Stoykov 10 years ago.
Prevent maximum recursion depth exceeded in templates

Download all attachments as: .zip

Change History (7)

by Venelin Stoykov, 10 years ago

Prevent maximum recursion depth exceeded in templates

comment:1 by Venelin Stoykov, 10 years ago

Description: modified (diff)

comment:2 by Tim Graham, 10 years ago

Needs tests: set
Triage Stage: UnreviewedAccepted

I haven't verified that the issue isn't a bug in the custom include tag, but tests are at least needed before we can commit this.

comment:3 by Tim Graham, 10 years ago

Component: UncategorizedTemplate system

comment:4 by Venelin Stoykov, 10 years ago

I updated my pull request with very specific test which describes the problem which can lead in some circumstances to the "maximum recursion depth exceeded" error.

comment:5 by Tim Graham <timograham@…>, 9 years ago

Resolution: fixed
Status: newclosed

In 0808ccc:

Fixed #23441, #24555 -- Improved the behavior of InclusionNode.

This change:

  • Makes the InclusionNode cache-safe by removing render-time side effects to its nodelist.
  • Ensures the render_context stack is properly scoped and reset by updating the render call to use Template.render rather than Nodelist.render.

comment:6 by Tim Graham <timograham@…>, 9 years ago

In 5c63c45:

[1.8.x] Fixed #23441, #24555 -- Improved the behavior of InclusionNode.

This change:

  • Makes the InclusionNode cache-safe by removing render-time side effects to its nodelist.
  • Ensures the render_context stack is properly scoped and reset by updating the render call to use Template.render rather than Nodelist.render.

Backport of 0808ccce3808235c5b5a56e3f689cec0d4bc0ebf from master

Note: See TracTickets for help on using tickets.
Back to Top