mirror of https://github.com/django/django.git
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:
parent
59b0c48ce2
commit
8b9b8d3bda
|
@ -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)
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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,8 +63,8 @@ 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):
|
||||||
response["Content-Length"] = statobj.st_size
|
response["Content-Length"] = statobj.st_size
|
||||||
|
|
|
@ -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():
|
self.assertEqual(b''.join(r), b'helloworld')
|
||||||
warnings.simplefilter("ignore", DeprecationWarning)
|
self.assertEqual(r.content, b'helloworld')
|
||||||
self.assertEqual(b''.join(r), b'helloworld')
|
|
||||||
self.assertEqual(r.content, b'') # not the expected result!
|
|
||||||
|
|
||||||
# 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('!')
|
||||||
|
|
Loading…
Reference in New Issue