summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Merickel <michael@merickel.org>2015-02-06 01:56:27 -0600
committerMichael Merickel <michael@merickel.org>2015-02-06 01:56:50 -0600
commitdd22319b1b8df9b1772451035fca582bd666e218 (patch)
treeb9051238ec2506f9d1699cc7ea3f6273317dd85f
parent4b13e0dfcdf47b3ef9befd529c0d78af8a2712d5 (diff)
downloadpyramid-dd22319b1b8df9b1772451035fca582bd666e218.tar.gz
pyramid-dd22319b1b8df9b1772451035fca582bd666e218.tar.bz2
pyramid-dd22319b1b8df9b1772451035fca582bd666e218.zip
update render_to_response to prevent renderers from mutating request.response
fixes #1536
-rw-r--r--pyramid/renderers.py43
-rw-r--r--pyramid/tests/test_renderers.py25
2 files changed, 53 insertions, 15 deletions
diff --git a/pyramid/renderers.py b/pyramid/renderers.py
index 3c35551ea..c2be8c2eb 100644
--- a/pyramid/renderers.py
+++ b/pyramid/renderers.py
@@ -1,3 +1,4 @@
+import contextlib
import json
import os
@@ -73,20 +74,8 @@ def render(renderer_name, value, request=None, package=None):
helper = RendererHelper(name=renderer_name, package=package,
registry=registry)
- saved_response = None
- # save the current response, preventing the renderer from affecting it
- attrs = request.__dict__ if request is not None else {}
- if 'response' in attrs:
- saved_response = attrs['response']
- del attrs['response']
-
- result = helper.render(value, None, request=request)
-
- # restore the original response, overwriting any changes
- if saved_response is not None:
- attrs['response'] = saved_response
- elif 'response' in attrs:
- del attrs['response']
+ with temporary_response(request):
+ result = helper.render(value, None, request=request)
return result
@@ -134,7 +123,31 @@ def render_to_response(renderer_name, value, request=None, package=None):
package = caller_package()
helper = RendererHelper(name=renderer_name, package=package,
registry=registry)
- return helper.render_to_response(value, None, request=request)
+
+ with temporary_response(request):
+ result = helper.render_to_response(value, None, request=request)
+
+ return result
+
+@contextlib.contextmanager
+def temporary_response(request):
+ """
+ Temporarily delete request.response and restore it afterward.
+ """
+ saved_response = None
+ # save the current response, preventing the renderer from affecting it
+ attrs = request.__dict__ if request is not None else {}
+ if 'response' in attrs:
+ saved_response = attrs['response']
+ del attrs['response']
+
+ yield
+
+ # restore the original response, overwriting any changes
+ if saved_response is not None:
+ attrs['response'] = saved_response
+ elif 'response' in attrs:
+ del attrs['response']
def get_renderer(renderer_name, package=None):
""" Return the renderer object for the renderer ``renderer_name``.
diff --git a/pyramid/tests/test_renderers.py b/pyramid/tests/test_renderers.py
index 6d79cc291..31e9d14f8 100644
--- a/pyramid/tests/test_renderers.py
+++ b/pyramid/tests/test_renderers.py
@@ -554,6 +554,31 @@ class Test_render_to_response(unittest.TestCase):
renderer.assert_(a=1)
renderer.assert_(request=request)
+ def test_response_preserved(self):
+ request = testing.DummyRequest()
+ response = object() # should error if mutated
+ request.response = response
+ # use a json renderer, which will mutate the response
+ result = self._callFUT('json', dict(a=1), request=request)
+ self.assertEqual(result.body, b'{"a": 1}')
+ self.assertNotEqual(request.response, result)
+ self.assertEqual(request.response, response)
+
+ def test_no_response_to_preserve(self):
+ from pyramid.decorator import reify
+ class DummyRequestWithClassResponse(object):
+ _response = DummyResponse()
+ _response.content_type = None
+ _response.default_content_type = None
+ @reify
+ def response(self):
+ return self._response
+ request = DummyRequestWithClassResponse()
+ # use a json renderer, which will mutate the response
+ result = self._callFUT('json', dict(a=1), request=request)
+ self.assertEqual(result.body, b'{"a": 1}')
+ self.assertFalse('response' in request.__dict__)
+
class Test_get_renderer(unittest.TestCase):
def setUp(self):
self.config = testing.setUp()