Commit 8b9b8d3b authored by Aymeric Augustin's avatar Aymeric Augustin
Browse files

Removed compatibility code for streaming responses.

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

Refs #6527, #7581.
parent 59b0c48c
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -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)
+2 −59
Original line number Diff line number Diff line
@@ -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:
            value = b''.join(self.make_bytes(e) for e in value)
        self._container = [value]
            self._base_content_is_iter = False

    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):
+4 −4
Original line number Diff line number Diff line
@@ -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,7 +63,7 @@ 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'),
    response = StreamingHttpResponse(open(fullpath, 'rb'),
                                     content_type=content_type)
    response["Last-Modified"] = http_date(statobj.st_mtime)
    if stat.S_ISREG(statobj.st_mode):
+6 −19
Original line number Diff line number Diff line
@@ -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(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('!')