diff --git a/django/template/context.py b/django/template/context.py index cf0c929065..1c8b7dacd9 100644 --- a/django/template/context.py +++ b/django/template/context.py @@ -14,14 +14,21 @@ class ContextPopException(Exception): "pop() has been called more times than push()" pass +class EmptyClass(object): + # No-op class which takes no args to its __init__ method, to help implement + # __copy__ + pass + class BaseContext(object): def __init__(self, dict_=None): dict_ = dict_ or {} self.dicts = [dict_] def __copy__(self): - duplicate = self._new() - duplicate.dicts = [dict_ for dict_ in self.dicts] + duplicate = EmptyClass() + duplicate.__class__ = self.__class__ + duplicate.__dict__ = self.__dict__.copy() + duplicate.dicts = duplicate.dicts[:] return duplicate def __repr__(self): @@ -31,9 +38,6 @@ class BaseContext(object): for d in reversed(self.dicts): yield d - def _new(self): - return self.__class__() - def push(self): d = {} self.dicts.append(d) @@ -88,11 +92,6 @@ class Context(BaseContext): duplicate.render_context = copy(self.render_context) return duplicate - def _new(self): - return self.__class__(autoescape=self.autoescape, - current_app=self.current_app, - use_l10n=self.use_l10n) - def update(self, other_dict): "Pushes other_dict to the stack of dictionaries in the Context" if not hasattr(other_dict, '__getitem__'): @@ -168,8 +167,3 @@ class RequestContext(Context): processors = tuple(processors) for processor in get_standard_processors() + processors: self.update(processor(request)) - - def _new(self): - return self.__class__(request=HttpRequest(), - current_app=self.current_app, - use_l10n=self.use_l10n) diff --git a/tests/regressiontests/test_client_regress/models.py b/tests/regressiontests/test_client_regress/models.py index 8f39961a82..4fbcc0dc15 100644 --- a/tests/regressiontests/test_client_regress/models.py +++ b/tests/regressiontests/test_client_regress/models.py @@ -9,6 +9,7 @@ from django.core.exceptions import SuspiciousOperation from django.core.urlresolvers import reverse from django.template import (TemplateDoesNotExist, TemplateSyntaxError, Context, Template, loader) +import django.template.context from django.test import Client, TestCase from django.test.client import encode_file from django.test.utils import ContextList @@ -648,6 +649,17 @@ class ContextTests(TestCase): except KeyError, e: self.assertEquals(e.args[0], 'does-not-exist') + def test_15368(self): + # Need to insert a context processor that assumes certain things about + # the request instance. This triggers a bug caused by some ways of + # copying RequestContext. + try: + django.template.context._standard_context_processors = (lambda request: {'path': request.special_path},) + response = self.client.get("/test_client_regress/request_context_view/") + self.assertContains(response, 'Path: /test_client_regress/request_context_view/') + finally: + django.template.context._standard_context_processors = None + class SessionTests(TestCase): fixtures = ['testdata.json'] diff --git a/tests/regressiontests/test_client_regress/templates/request_context.html b/tests/regressiontests/test_client_regress/templates/request_context.html new file mode 100644 index 0000000000..37a2425c09 --- /dev/null +++ b/tests/regressiontests/test_client_regress/templates/request_context.html @@ -0,0 +1 @@ +Path: {{ path }} diff --git a/tests/regressiontests/test_client_regress/urls.py b/tests/regressiontests/test_client_regress/urls.py index 31edc96a86..162befe73e 100644 --- a/tests/regressiontests/test_client_regress/urls.py +++ b/tests/regressiontests/test_client_regress/urls.py @@ -27,4 +27,5 @@ urlpatterns = patterns('', (r'^check_headers/$', views.check_headers), (r'^check_headers_redirect/$', redirect_to, {'url': '/test_client_regress/check_headers/'}), (r'^raw_post_data/$', views.raw_post_data), + (r'^request_context_view/$', views.request_context_view), ) diff --git a/tests/regressiontests/test_client_regress/views.py b/tests/regressiontests/test_client_regress/views.py index 9fcb894fe0..c40f34fe56 100644 --- a/tests/regressiontests/test_client_regress/views.py +++ b/tests/regressiontests/test_client_regress/views.py @@ -7,6 +7,7 @@ from django.utils import simplejson from django.utils.encoding import smart_str from django.core.serializers.json import DjangoJSONEncoder from django.test.client import CONTENT_TYPE_RE +from django.template import RequestContext def no_template_view(request): "A simple view that expects a GET request, and returns a rendered template" @@ -94,3 +95,8 @@ def check_headers(request): def raw_post_data(request): "A view that is requested with GET and accesses request.raw_post_data. Refs #14753." return HttpResponse(request.raw_post_data) + +def request_context_view(request): + # Special attribute that won't be present on a plain HttpRequest + request.special_path = request.path + return render_to_response('request_context.html', context_instance=RequestContext(request, {}))