From 82b3e6ffcb9d810cc0e3ec27d25f89ce1fd525e0 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Wed, 24 Oct 2012 11:33:56 +0200 Subject: [PATCH] Fixed #13222 -- Made HttpResponse iterable once response.content can be accessed many times as desired, and always returns the same result. iter(response) works only once and consumes the iterator. --- django/http/response.py | 5 +++-- django/test/testcases.py | 6 +++++- tests/regressiontests/httpwrappers/tests.py | 23 ++++++++++++++++++++- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/django/http/response.py b/django/http/response.py index e9cc3f70a96..6bbadd7e2b3 100644 --- a/django/http/response.py +++ b/django/http/response.py @@ -283,7 +283,8 @@ class HttpResponse(HttpResponseBase): 'deprecated. Use `StreamingHttpResponse` instead ' 'if you need the streaming behavior.', PendingDeprecationWarning, stacklevel=2) - self._iterator = iter(self._container) + if not hasattr(self, '_iterator'): + self._iterator = iter(self._container) return self def __next__(self): @@ -303,7 +304,7 @@ class HttpResponse(HttpResponseBase): def tell(self): self._consume_content() - return sum(len(chunk) for chunk in self) + return len(self.content) class StreamingHttpResponse(HttpResponseBase): diff --git a/django/test/testcases.py b/django/test/testcases.py index cfa2cde643c..1239275264b 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -596,7 +596,11 @@ class TransactionTestCase(SimpleTestCase): msg_prefix + "Couldn't retrieve content: Response code was %d" " (expected %d)" % (response.status_code, status_code)) text = force_text(text, encoding=response._charset) - content = b''.join(response).decode(response._charset) + if response.streaming: + content = b''.join(response.streaming_content) + else: + content = response.content + content = content.decode(response._charset) # Avoid ResourceWarning about unclosed files. response.close() if html: diff --git a/tests/regressiontests/httpwrappers/tests.py b/tests/regressiontests/httpwrappers/tests.py index 7f61a3074fa..8e20c80de31 100644 --- a/tests/regressiontests/httpwrappers/tests.py +++ b/tests/regressiontests/httpwrappers/tests.py @@ -337,15 +337,36 @@ class HttpResponseTests(unittest.TestCase): self.assertRaises(UnicodeEncodeError, getattr, r, 'content') - # content can safely be accessed multiple times. + # .content can safely be accessed multiple times. r = HttpResponse(iter(['hello', 'world'])) self.assertEqual(r.content, r.content) self.assertEqual(r.content, b'helloworld') + # accessing the iterator works (once) after accessing .content + self.assertEqual(b''.join(r), b'helloworld') + self.assertEqual(b''.join(r), b'') + # accessing .content still works + self.assertEqual(r.content, b'helloworld') + + # XXX accessing .content doesn't work if the response was iterated first + # XXX change this when the deprecation completes in HttpResponse + r = HttpResponse(iter(['hello', 'world'])) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", PendingDeprecationWarning) + self.assertEqual(b''.join(r), b'helloworld') + self.assertEqual(r.content, b'') # not the expected result! # additional content can be written to the response. + r = HttpResponse(iter(['hello', 'world'])) + self.assertEqual(r.content, b'helloworld') r.write('!') self.assertEqual(r.content, b'helloworld!') + def test_iterator_isnt_rewound(self): + # Regression test for #13222 + r = HttpResponse('abc') + i = iter(r) + self.assertEqual(list(i), [b'abc']) + self.assertEqual(list(i), []) def test_file_interface(self): r = HttpResponse()