From 8b9b8d3bda09eb1b447631182d06c6c5e51425f6 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sat, 29 Jun 2013 15:41:57 +0200 Subject: [PATCH] Removed compatibility code for streaming responses. This code provided a deprecation path for old-style streaming responses. Refs #6527, #7581. --- django/http/__init__.py | 8 ++--- django/http/response.py | 61 ++----------------------------------- django/views/static.py | 8 ++--- tests/httpwrappers/tests.py | 25 ++++----------- 4 files changed, 16 insertions(+), 86 deletions(-) diff --git a/django/http/__init__.py b/django/http/__init__.py index 46afa34ee73..be7b24b2e2f 100644 --- a/django/http/__init__.py +++ b/django/http/__init__.py @@ -2,9 +2,9 @@ from django.http.cookie import SimpleCookie, parse_cookie from django.http.request import (HttpRequest, QueryDict, UnreadablePostError, build_request_repr) from django.http.response import (HttpResponse, StreamingHttpResponse, - CompatibleStreamingHttpResponse, HttpResponsePermanentRedirect, - HttpResponseRedirect, HttpResponseNotModified, HttpResponseBadRequest, - HttpResponseForbidden, HttpResponseNotFound, HttpResponseNotAllowed, - HttpResponseGone, HttpResponseServerError, Http404, BadHeaderError) + HttpResponseRedirect, HttpResponsePermanentRedirect, + HttpResponseNotModified, HttpResponseBadRequest, HttpResponseForbidden, + HttpResponseNotFound, HttpResponseNotAllowed, HttpResponseGone, + HttpResponseServerError, Http404, BadHeaderError) from django.http.utils import (fix_location_header, conditional_content_removal, fix_IE_for_attach, fix_IE_for_vary) diff --git a/django/http/response.py b/django/http/response.py index 552161c2123..1f0849c0bf5 100644 --- a/django/http/response.py +++ b/django/http/response.py @@ -2,7 +2,6 @@ from __future__ import absolute_import, unicode_literals import datetime import time -import warnings from email.header import Header try: from urllib.parse import urlparse @@ -332,53 +331,27 @@ class HttpResponse(HttpResponseBase): else: __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 def content(self): - self._consume_content() return b''.join(self.make_bytes(e) for e in self._container) @content.setter def content(self, value): 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'): self._closable_objects.append(value) - else: - self._container = [value] - self._base_content_is_iter = False + value = b''.join(self.make_bytes(e) for e in value) + self._container = [value] 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'): self._iterator = iter(self._container) return self def write(self, content): - self._consume_content() self._container.append(content) def tell(self): - self._consume_content() return len(self.content) @@ -416,35 +389,6 @@ class StreamingHttpResponse(HttpResponseBase): 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): allowed_schemes = ['http', 'https', 'ftp'] @@ -478,7 +422,6 @@ class HttpResponseNotModified(HttpResponse): if value: raise AttributeError("You cannot set content to a 304 (Not Modified) response") self._container = [] - self._base_content_is_iter = False class HttpResponseBadRequest(HttpResponse): diff --git a/django/views/static.py b/django/views/static.py index 12af1307965..470999bb9d0 100644 --- a/django/views/static.py +++ b/django/views/static.py @@ -14,8 +14,8 @@ try: except ImportError: # Python 2 from urllib import unquote -from django.http import (CompatibleStreamingHttpResponse, Http404, - HttpResponse, HttpResponseRedirect, HttpResponseNotModified) +from django.http import (Http404, HttpResponse, HttpResponseRedirect, + HttpResponseNotModified, StreamingHttpResponse) from django.template import loader, Template, Context, TemplateDoesNotExist from django.utils.http import http_date, parse_http_date from django.utils.translation import ugettext as _, ugettext_noop @@ -63,8 +63,8 @@ def serve(request, path, document_root=None, show_indexes=False): return HttpResponseNotModified() content_type, encoding = mimetypes.guess_type(fullpath) content_type = content_type or 'application/octet-stream' - response = CompatibleStreamingHttpResponse(open(fullpath, 'rb'), - content_type=content_type) + response = StreamingHttpResponse(open(fullpath, 'rb'), + content_type=content_type) response["Last-Modified"] = http_date(statobj.st_mtime) if stat.S_ISREG(statobj.st_mode): response["Content-Length"] = statobj.st_size diff --git a/tests/httpwrappers/tests.py b/tests/httpwrappers/tests.py index 194232e92fa..673409fd99c 100644 --- a/tests/httpwrappers/tests.py +++ b/tests/httpwrappers/tests.py @@ -324,19 +324,10 @@ class HttpResponseTests(unittest.TestCase): r.content = [1, 2, 3] self.assertEqual(r.content, b'123') - #test retrieval explicitly using iter (deprecated) and odd inputs + #test odd inputs r = HttpResponse() 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') - self.assertEqual(result, [b'1', b'2', b'3', b'\xde\x9e']) self.assertEqual(r.content, b'123\xde\x9e') #with Content-Encoding header @@ -344,9 +335,8 @@ class HttpResponseTests(unittest.TestCase): r['Content-Encoding'] = 'winning' r.content = [b'abc', b'def'] self.assertEqual(r.content, b'abcdef') - r.content = ['\u079e'] self.assertRaises(TypeError if six.PY3 else UnicodeEncodeError, - getattr, r, 'content') + setattr, r, 'content', ['\u079e']) # .content can safely be accessed multiple times. r = HttpResponse(iter(['hello', 'world'])) @@ -358,15 +348,12 @@ class HttpResponseTests(unittest.TestCase): # 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 + # Accessing .content also works if the response was iterated first. r = HttpResponse(iter(['hello', 'world'])) - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - self.assertEqual(b''.join(r), b'helloworld') - self.assertEqual(r.content, b'') # not the expected result! + self.assertEqual(b''.join(r), b'helloworld') + 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'])) self.assertEqual(r.content, b'helloworld') r.write('!')