summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohannes Hoppe <info@johanneshoppe.com>2015-11-11 20:17:32 +0100
committerTim Graham <timograham@gmail.com>2015-12-14 12:46:48 -0500
commit5233b70070f8979f41ca1da2c1b1d78c8e30944e (patch)
tree54993a920bb9df5cdfbbc92dc4fbb010a1d662a5
parenta6c803a2e3d270818d20f3f0c76e2241de9f0ab1 (diff)
Fixed #25725 -- Made HttpReponse immediately close objects.
-rw-r--r--django/http/response.py11
-rw-r--r--docs/ref/request-response.txt8
-rw-r--r--docs/releases/1.10.txt4
-rw-r--r--tests/httpwrappers/tests.py13
-rw-r--r--tests/responses/tests.py11
5 files changed, 29 insertions, 18 deletions
diff --git a/django/http/response.py b/django/http/response.py
index 94d14ddb54..da9f9ef6a6 100644
--- a/django/http/response.py
+++ b/django/http/response.py
@@ -316,13 +316,16 @@ class HttpResponse(HttpResponseBase):
def content(self, value):
# Consume iterators upon assignment to allow repeated iteration.
if hasattr(value, '__iter__') and not isinstance(value, (bytes, six.string_types)):
+ content = b''.join(self.make_bytes(chunk) for chunk in value)
if hasattr(value, 'close'):
- self._closable_objects.append(value)
- value = b''.join(self.make_bytes(chunk) for chunk in value)
+ try:
+ value.close()
+ except Exception:
+ pass
else:
- value = self.make_bytes(value)
+ content = self.make_bytes(value)
# Create a list of properly encoded bytestrings to support write().
- self._container = [value]
+ self._container = [content]
def __iter__(self):
return iter(self._container)
diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt
index 81f992a778..675a47d8d0 100644
--- a/docs/ref/request-response.txt
+++ b/docs/ref/request-response.txt
@@ -608,11 +608,17 @@ Passing iterators
Finally, you can pass ``HttpResponse`` an iterator rather than strings.
``HttpResponse`` will consume the iterator immediately, store its content as a
-string, and discard it.
+string, and discard it. Objects with a ``close()`` method such as files and
+generators are immediately closed.
If you need the response to be streamed from the iterator to the client, you
must use the :class:`StreamingHttpResponse` class instead.
+.. versionchanged:: 1.10
+
+ Objects with a ``close()`` method used to be closed when the WSGI server
+ called ``close()`` on the response.
+
Setting header fields
~~~~~~~~~~~~~~~~~~~~~
diff --git a/docs/releases/1.10.txt b/docs/releases/1.10.txt
index 6f94154929..d932887a51 100644
--- a/docs/releases/1.10.txt
+++ b/docs/releases/1.10.txt
@@ -357,6 +357,10 @@ Miscellaneous
* The ``add_postgis_srs()`` backwards compatibility alias for
``django.contrib.gis.utils.add_srs_entry()`` is removed.
+* Objects with a ``close()`` method such as files and generators passed to
+ :class:`~django.http.HttpResponse` are now closed immediately instead of when
+ the WSGI server calls ``close()`` on the response.
+
.. _deprecated-features-1.10:
Features deprecated in 1.10
diff --git a/tests/httpwrappers/tests.py b/tests/httpwrappers/tests.py
index 94d269c90b..53dd58da3b 100644
--- a/tests/httpwrappers/tests.py
+++ b/tests/httpwrappers/tests.py
@@ -588,18 +588,8 @@ class FileCloseTests(SimpleTestCase):
# file isn't closed until we close the response.
file1 = open(filename)
r = HttpResponse(file1)
- self.assertFalse(file1.closed)
- r.close()
self.assertTrue(file1.closed)
-
- # don't automatically close file when we finish iterating the response.
- file1 = open(filename)
- r = HttpResponse(file1)
- self.assertFalse(file1.closed)
- list(r)
- self.assertFalse(file1.closed)
r.close()
- self.assertTrue(file1.closed)
# when multiple file are assigned as content, make sure they are all
# closed with the response.
@@ -607,9 +597,6 @@ class FileCloseTests(SimpleTestCase):
file2 = open(filename)
r = HttpResponse(file1)
r.content = file2
- self.assertFalse(file1.closed)
- self.assertFalse(file2.closed)
- r.close()
self.assertTrue(file1.closed)
self.assertTrue(file2.closed)
diff --git a/tests/responses/tests.py b/tests/responses/tests.py
index 6d0588daa7..5f7a0b5e12 100644
--- a/tests/responses/tests.py
+++ b/tests/responses/tests.py
@@ -5,6 +5,7 @@ from __future__ import unicode_literals
import io
from django.conf import settings
+from django.core.cache import cache
from django.http import HttpResponse
from django.http.response import HttpResponseBase
from django.test import SimpleTestCase
@@ -121,3 +122,13 @@ class HttpResponseTests(SimpleTestCase):
with io.TextIOWrapper(r, UTF8) as buf:
buf.write(content)
self.assertEqual(r.content, content.encode(UTF8))
+
+ def test_generator_cache(self):
+ generator = ("{}".format(i) for i in range(10))
+ response = HttpResponse(content=generator)
+ self.assertEqual(response.content, b'0123456789')
+ self.assertRaises(StopIteration, next, generator)
+
+ cache.set('my-response-key', response)
+ response = cache.get('my-response-key')
+ self.assertEqual(response.content, b'0123456789')