From 51b606f75d3662cf6736ade400d6423cb519968b Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Thu, 19 Feb 2015 23:20:27 +0100 Subject: [PATCH] Removed a non-obvious side-effect of assigning Context.template. Explicit is better than implicit. --- django/template/base.py | 14 ++++------ django/template/context.py | 55 ++++++++++++++++++++++---------------- 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/django/template/base.py b/django/template/base.py index 325cd7e560..d89021d7c0 100644 --- a/django/template/base.py +++ b/django/template/base.py @@ -204,19 +204,15 @@ class Template(object): 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): diff --git a/django/template/context.py b/django/template/context.py index b4983d1909..af4fb8853b 100644 --- a/django/template/context.py +++ b/django/template/context.py @@ -1,4 +1,5 @@ import warnings +from contextlib import contextmanager from copy import copy from django.utils.deprecation import RemovedInDjango20Warning @@ -134,8 +135,8 @@ class Context(BaseContext): 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_) @@ -143,6 +144,16 @@ class Context(BaseContext): 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) @@ -210,28 +221,26 @@ class RequestContext(Context): self._processors_index = len(self.dicts) self.update({}) # placeholder for context processors output - @property - def template(self): - return self._template + @contextmanager + def bind_template(self, template): + if self.template is not None: + raise RuntimeError("Context is already bound to a 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 + 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)