Fixed #25725 -- Made HttpReponse immediately close objects.
This commit is contained in:
parent
a6c803a2e3
commit
5233b70070
|
@ -316,13 +316,16 @@ class HttpResponse(HttpResponseBase):
|
||||||
def content(self, value):
|
def content(self, value):
|
||||||
# Consume iterators upon assignment to allow repeated iteration.
|
# Consume iterators upon assignment to allow repeated iteration.
|
||||||
if hasattr(value, '__iter__') and not isinstance(value, (bytes, six.string_types)):
|
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'):
|
if hasattr(value, 'close'):
|
||||||
self._closable_objects.append(value)
|
try:
|
||||||
value = b''.join(self.make_bytes(chunk) for chunk in value)
|
value.close()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
else:
|
else:
|
||||||
value = self.make_bytes(value)
|
content = self.make_bytes(value)
|
||||||
# Create a list of properly encoded bytestrings to support write().
|
# Create a list of properly encoded bytestrings to support write().
|
||||||
self._container = [value]
|
self._container = [content]
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return iter(self._container)
|
return iter(self._container)
|
||||||
|
|
|
@ -608,11 +608,17 @@ Passing iterators
|
||||||
|
|
||||||
Finally, you can pass ``HttpResponse`` an iterator rather than strings.
|
Finally, you can pass ``HttpResponse`` an iterator rather than strings.
|
||||||
``HttpResponse`` will consume the iterator immediately, store its content as a
|
``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
|
If you need the response to be streamed from the iterator to the client, you
|
||||||
must use the :class:`StreamingHttpResponse` class instead.
|
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
|
Setting header fields
|
||||||
~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -357,6 +357,10 @@ Miscellaneous
|
||||||
* The ``add_postgis_srs()`` backwards compatibility alias for
|
* The ``add_postgis_srs()`` backwards compatibility alias for
|
||||||
``django.contrib.gis.utils.add_srs_entry()`` is removed.
|
``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:
|
.. _deprecated-features-1.10:
|
||||||
|
|
||||||
Features deprecated in 1.10
|
Features deprecated in 1.10
|
||||||
|
|
|
@ -588,18 +588,8 @@ class FileCloseTests(SimpleTestCase):
|
||||||
# file isn't closed until we close the response.
|
# file isn't closed until we close the response.
|
||||||
file1 = open(filename)
|
file1 = open(filename)
|
||||||
r = HttpResponse(file1)
|
r = HttpResponse(file1)
|
||||||
self.assertFalse(file1.closed)
|
|
||||||
r.close()
|
|
||||||
self.assertTrue(file1.closed)
|
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()
|
r.close()
|
||||||
self.assertTrue(file1.closed)
|
|
||||||
|
|
||||||
# when multiple file are assigned as content, make sure they are all
|
# when multiple file are assigned as content, make sure they are all
|
||||||
# closed with the response.
|
# closed with the response.
|
||||||
|
@ -607,9 +597,6 @@ class FileCloseTests(SimpleTestCase):
|
||||||
file2 = open(filename)
|
file2 = open(filename)
|
||||||
r = HttpResponse(file1)
|
r = HttpResponse(file1)
|
||||||
r.content = file2
|
r.content = file2
|
||||||
self.assertFalse(file1.closed)
|
|
||||||
self.assertFalse(file2.closed)
|
|
||||||
r.close()
|
|
||||||
self.assertTrue(file1.closed)
|
self.assertTrue(file1.closed)
|
||||||
self.assertTrue(file2.closed)
|
self.assertTrue(file2.closed)
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ from __future__ import unicode_literals
|
||||||
import io
|
import io
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.core.cache import cache
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.http.response import HttpResponseBase
|
from django.http.response import HttpResponseBase
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase
|
||||||
|
@ -121,3 +122,13 @@ class HttpResponseTests(SimpleTestCase):
|
||||||
with io.TextIOWrapper(r, UTF8) as buf:
|
with io.TextIOWrapper(r, UTF8) as buf:
|
||||||
buf.write(content)
|
buf.write(content)
|
||||||
self.assertEqual(r.content, content.encode(UTF8))
|
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')
|
||||||
|
|
Loading…
Reference in New Issue