Skip to content

Commit

Permalink
Removed a non-obvious side-effect of assigning Context.template.
Browse files Browse the repository at this point in the history
Explicit is better than implicit.
  • Loading branch information
aaugustin committed Feb 20, 2015
1 parent e43f99d commit 51b606f
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 33 deletions.
14 changes: 5 additions & 9 deletions django/template/base.py
Expand Up @@ -204,19 +204,15 @@ def _render(self, context):

def render(self, context):
"Display stage -- can be called many times"
# Set context.template to the original template -- as opposed to
# extended or included templates -- during rendering. This may be
# used for accessing context.template.engine.
toplevel_render = context.template is None
if toplevel_render:
context.template = self
context.render_context.push()
try:
return self._render(context)
if context.template is None:
with context.bind_template(self):
return self._render(context)
else:
return self._render(context)
finally:
context.render_context.pop()
if toplevel_render:
context.template = None


class Token(object):
Expand Down
57 changes: 33 additions & 24 deletions django/template/context.py
@@ -1,4 +1,5 @@
import warnings
from contextlib import contextmanager
from copy import copy

from django.utils.deprecation import RemovedInDjango20Warning
Expand Down Expand Up @@ -134,15 +135,25 @@ def __init__(self, dict_=None, autoescape=True,
self.use_l10n = use_l10n
self.use_tz = use_tz
self.render_context = RenderContext()
# Set to the original template during rendering -- as opposed to
# extended or included templates
# Set to the original template -- as opposed to extended or included
# templates -- during rendering, see bind_template.
self.template = None
super(Context, self).__init__(dict_)

@property
def current_app(self):
return None if self._current_app is _current_app_undefined else self._current_app

@contextmanager
def bind_template(self, template):
if self.template is not None:
raise RuntimeError("Context is already bound to a template")
self.template = template
try:
yield
finally:
self.template = None

def __copy__(self):
duplicate = super(Context, self).__copy__()
duplicate.render_context = copy(self.render_context)
Expand Down Expand Up @@ -210,28 +221,26 @@ def __init__(self, request, dict_=None, processors=None,
self._processors_index = len(self.dicts)
self.update({}) # placeholder for context processors output

@property
def template(self):
return self._template

@template.setter
def template(self, template):
# Execute context processors when Template.render(self, context) sets
# context.template = self. Until then, since the context isn't tied to
# an engine, it has no way to know which context processors to apply.
self._template = template
if hasattr(self, '_processors_index'):
if template is None:
# Unset context processors.
self.dicts[self._processors_index] = {}
else:
# Set context processors for this engine.
processors = (template.engine.template_context_processors +
self._processors)
updates = {}
for processor in processors:
updates.update(processor(self.request))
self.dicts[self._processors_index] = updates
@contextmanager
def bind_template(self, template):
if self.template is not None:
raise RuntimeError("Context is already bound to a template")

self.template = template
# Set context processors according to the template engine's settings.
processors = (template.engine.template_context_processors +
self._processors)
updates = {}
for processor in processors:
updates.update(processor(self.request))
self.dicts[self._processors_index] = updates

try:
yield
finally:
self.template = None
# Unset context processors.
self.dicts[self._processors_index] = {}

def new(self, values=None):
new_context = super(RequestContext, self).new(values)
Expand Down

0 comments on commit 51b606f

Please sign in to comment.