Removed compatibility code for streaming responses.

This code provided a deprecation path for old-style streaming responses.

Refs #6527, #7581.
This commit is contained in:
Aymeric Augustin 2013-06-29 15:41:57 +02:00
parent 59b0c48ce2
commit 8b9b8d3bda
4 changed files with 16 additions and 86 deletions

View File

@ -2,9 +2,9 @@ from django.http.cookie import SimpleCookie, parse_cookie
from django.http.request import (HttpRequest, QueryDict, UnreadablePostError, from django.http.request import (HttpRequest, QueryDict, UnreadablePostError,
build_request_repr) build_request_repr)
from django.http.response import (HttpResponse, StreamingHttpResponse, from django.http.response import (HttpResponse, StreamingHttpResponse,
CompatibleStreamingHttpResponse, HttpResponsePermanentRedirect, HttpResponseRedirect, HttpResponsePermanentRedirect,
HttpResponseRedirect, HttpResponseNotModified, HttpResponseBadRequest, HttpResponseNotModified, HttpResponseBadRequest, HttpResponseForbidden,
HttpResponseForbidden, HttpResponseNotFound, HttpResponseNotAllowed, HttpResponseNotFound, HttpResponseNotAllowed, HttpResponseGone,
HttpResponseGone, HttpResponseServerError, Http404, BadHeaderError) HttpResponseServerError, Http404, BadHeaderError)
from django.http.utils import (fix_location_header, conditional_content_removal, from django.http.utils import (fix_location_header, conditional_content_removal,
fix_IE_for_attach, fix_IE_for_vary) fix_IE_for_attach, fix_IE_for_vary)

View File

@ -2,7 +2,6 @@ from __future__ import absolute_import, unicode_literals
import datetime import datetime
import time import time
import warnings
from email.header import Header from email.header import Header
try: try:
from urllib.parse import urlparse from urllib.parse import urlparse
@ -332,53 +331,27 @@ class HttpResponse(HttpResponseBase):
else: else:
__str__ = serialize __str__ = serialize
def _consume_content(self):
# If the response was instantiated with an iterator, when its content
# is accessed, the iterator is going be exhausted and the content
# loaded in memory. At this point, it's better to abandon the original
# iterator and save the content for later reuse. This is a temporary
# solution. See the comment in __iter__ below for the long term plan.
if self._base_content_is_iter:
self.content = b''.join(self.make_bytes(e) for e in self._container)
@property @property
def content(self): def content(self):
self._consume_content()
return b''.join(self.make_bytes(e) for e in self._container) return b''.join(self.make_bytes(e) for e in self._container)
@content.setter @content.setter
def content(self, value): def content(self, value):
if hasattr(value, '__iter__') and not isinstance(value, (bytes, six.string_types)): if hasattr(value, '__iter__') and not isinstance(value, (bytes, six.string_types)):
self._container = value
self._base_content_is_iter = True
if hasattr(value, 'close'): if hasattr(value, 'close'):
self._closable_objects.append(value) self._closable_objects.append(value)
else: value = b''.join(self.make_bytes(e) for e in value)
self._container = [value] self._container = [value]
self._base_content_is_iter = False
def __iter__(self): def __iter__(self):
# Raise a deprecation warning only if the content wasn't consumed yet,
# because the response may be intended to be streamed.
# Once the deprecation completes, iterators should be consumed upon
# assignment rather than upon access. The _consume_content method
# should be removed. See #6527.
if self._base_content_is_iter:
warnings.warn(
'Creating streaming responses with `HttpResponse` is '
'deprecated. Use `StreamingHttpResponse` instead '
'if you need the streaming behavior.',
DeprecationWarning, stacklevel=2)
if not hasattr(self, '_iterator'): if not hasattr(self, '_iterator'):
self._iterator = iter(self._container) self._iterator = iter(self._container)
return self return self
def write(self, content): def write(self, content):
self._consume_content()
self._container.append(content) self._container.append(content)
def tell(self): def tell(self):
self._consume_content()
return len(self.content) return len(self.content)
@ -416,35 +389,6 @@ class StreamingHttpResponse(HttpResponseBase):
self._closable_objects.append(value) self._closable_objects.append(value)
class CompatibleStreamingHttpResponse(StreamingHttpResponse):
"""
This class maintains compatibility with middleware that doesn't know how
to handle the content of a streaming response by exposing a `content`
attribute that will consume and cache the content iterator when accessed.
These responses will stream only if no middleware attempts to access the
`content` attribute. Otherwise, they will behave like a regular response,
and raise a `DeprecationWarning`.
"""
@property
def content(self):
warnings.warn(
'Accessing the `content` attribute on a streaming response is '
'deprecated. Use the `streaming_content` attribute instead.',
DeprecationWarning, stacklevel=2)
content = b''.join(self)
self.streaming_content = [content]
return content
@content.setter
def content(self, content):
warnings.warn(
'Accessing the `content` attribute on a streaming response is '
'deprecated. Use the `streaming_content` attribute instead.',
DeprecationWarning, stacklevel=2)
self.streaming_content = [content]
class HttpResponseRedirectBase(HttpResponse): class HttpResponseRedirectBase(HttpResponse):
allowed_schemes = ['http', 'https', 'ftp'] allowed_schemes = ['http', 'https', 'ftp']
@ -478,7 +422,6 @@ class HttpResponseNotModified(HttpResponse):
if value: if value:
raise AttributeError("You cannot set content to a 304 (Not Modified) response") raise AttributeError("You cannot set content to a 304 (Not Modified) response")
self._container = [] self._container = []
self._base_content_is_iter = False
class HttpResponseBadRequest(HttpResponse): class HttpResponseBadRequest(HttpResponse):

View File

@ -14,8 +14,8 @@ try:
except ImportError: # Python 2 except ImportError: # Python 2
from urllib import unquote from urllib import unquote
from django.http import (CompatibleStreamingHttpResponse, Http404, from django.http import (Http404, HttpResponse, HttpResponseRedirect,
HttpResponse, HttpResponseRedirect, HttpResponseNotModified) HttpResponseNotModified, StreamingHttpResponse)
from django.template import loader, Template, Context, TemplateDoesNotExist from django.template import loader, Template, Context, TemplateDoesNotExist
from django.utils.http import http_date, parse_http_date from django.utils.http import http_date, parse_http_date
from django.utils.translation import ugettext as _, ugettext_noop from django.utils.translation import ugettext as _, ugettext_noop
@ -63,7 +63,7 @@ def serve(request, path, document_root=None, show_indexes=False):
return HttpResponseNotModified() return HttpResponseNotModified()
content_type, encoding = mimetypes.guess_type(fullpath) content_type, encoding = mimetypes.guess_type(fullpath)
content_type = content_type or 'application/octet-stream' content_type = content_type or 'application/octet-stream'
response = CompatibleStreamingHttpResponse(open(fullpath, 'rb'), response = StreamingHttpResponse(open(fullpath, 'rb'),
content_type=content_type) content_type=content_type)
response["Last-Modified"] = http_date(statobj.st_mtime) response["Last-Modified"] = http_date(statobj.st_mtime)
if stat.S_ISREG(statobj.st_mode): if stat.S_ISREG(statobj.st_mode):

View File

@ -324,19 +324,10 @@ class HttpResponseTests(unittest.TestCase):
r.content = [1, 2, 3] r.content = [1, 2, 3]
self.assertEqual(r.content, b'123') self.assertEqual(r.content, b'123')
#test retrieval explicitly using iter (deprecated) and odd inputs #test odd inputs
r = HttpResponse() r = HttpResponse()
r.content = ['1', '2', 3, '\u079e'] r.content = ['1', '2', 3, '\u079e']
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always", DeprecationWarning)
my_iter = iter(r)
self.assertEqual(w[0].category, DeprecationWarning)
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always", DeprecationWarning)
result = list(my_iter)
self.assertEqual(w[0].category, DeprecationWarning)
#'\xde\x9e' == unichr(1950).encode('utf-8') #'\xde\x9e' == unichr(1950).encode('utf-8')
self.assertEqual(result, [b'1', b'2', b'3', b'\xde\x9e'])
self.assertEqual(r.content, b'123\xde\x9e') self.assertEqual(r.content, b'123\xde\x9e')
#with Content-Encoding header #with Content-Encoding header
@ -344,9 +335,8 @@ class HttpResponseTests(unittest.TestCase):
r['Content-Encoding'] = 'winning' r['Content-Encoding'] = 'winning'
r.content = [b'abc', b'def'] r.content = [b'abc', b'def']
self.assertEqual(r.content, b'abcdef') self.assertEqual(r.content, b'abcdef')
r.content = ['\u079e']
self.assertRaises(TypeError if six.PY3 else UnicodeEncodeError, self.assertRaises(TypeError if six.PY3 else UnicodeEncodeError,
getattr, r, 'content') setattr, r, 'content', ['\u079e'])
# .content can safely be accessed multiple times. # .content can safely be accessed multiple times.
r = HttpResponse(iter(['hello', 'world'])) r = HttpResponse(iter(['hello', 'world']))
@ -358,15 +348,12 @@ class HttpResponseTests(unittest.TestCase):
# accessing .content still works # accessing .content still works
self.assertEqual(r.content, b'helloworld') self.assertEqual(r.content, b'helloworld')
# XXX accessing .content doesn't work if the response was iterated first # Accessing .content also works if the response was iterated first.
# XXX change this when the deprecation completes in HttpResponse
r = HttpResponse(iter(['hello', 'world'])) r = HttpResponse(iter(['hello', 'world']))
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
self.assertEqual(b''.join(r), b'helloworld') self.assertEqual(b''.join(r), b'helloworld')
self.assertEqual(r.content, b'') # not the expected result! self.assertEqual(r.content, b'helloworld')
# additional content can be written to the response. # Additional content can be written to the response.
r = HttpResponse(iter(['hello', 'world'])) r = HttpResponse(iter(['hello', 'world']))
self.assertEqual(r.content, b'helloworld') self.assertEqual(r.content, b'helloworld')
r.write('!') r.write('!')