diff options
| author | Michael Merickel <michael@merickel.org> | 2015-02-06 01:56:27 -0600 |
|---|---|---|
| committer | Michael Merickel <michael@merickel.org> | 2015-02-06 01:56:50 -0600 |
| commit | dd22319b1b8df9b1772451035fca582bd666e218 (patch) | |
| tree | b9051238ec2506f9d1699cc7ea3f6273317dd85f | |
| parent | 4b13e0dfcdf47b3ef9befd529c0d78af8a2712d5 (diff) | |
| download | pyramid-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.py | 43 | ||||
| -rw-r--r-- | pyramid/tests/test_renderers.py | 25 |
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() |
