From bc4638d722c0e48cfd6bc4d8084b41445f987004 Mon Sep 17 00:00:00 2001 From: Adrian Holovaty Date: Sun, 9 Apr 2006 23:54:34 +0000 Subject: [PATCH] Fixed #1569 -- HttpResponse now accepts iterators. Thanks, Maniac git-svn-id: http://code.djangoproject.com/svn/django/trunk@2639 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- AUTHORS | 2 +- django/core/handlers/modpython.py | 3 ++- django/core/handlers/wsgi.py | 3 +-- django/middleware/common.py | 2 +- django/utils/cache.py | 2 +- django/utils/httpwrappers.py | 41 ++++++++++++++++++++----------- docs/request_response.txt | 11 ++++++++- 7 files changed, 43 insertions(+), 21 deletions(-) diff --git a/AUTHORS b/AUTHORS index 1c8036b74a..edd7d50726 100644 --- a/AUTHORS +++ b/AUTHORS @@ -73,7 +73,6 @@ answer newbie questions, and generally made Django that much better: Eugene Lazutkin limodou Martin Maney - Maniac Manuzhai Petar Marić mark@junklight.com @@ -95,6 +94,7 @@ answer newbie questions, and generally made Django that much better: J. Rademaker Brian Ray Oliver Rutherfurd + Ivan Sagalaev (Maniac) David Schein sopel Radek Švarz diff --git a/django/core/handlers/modpython.py b/django/core/handlers/modpython.py index eec35ff072..37499c6f06 100644 --- a/django/core/handlers/modpython.py +++ b/django/core/handlers/modpython.py @@ -162,7 +162,8 @@ def populate_apache_request(http_response, mod_python_req): for c in http_response.cookies.values(): mod_python_req.headers_out.add('Set-Cookie', c.output(header='')) mod_python_req.status = http_response.status_code - mod_python_req.write(http_response.get_content_as_string(settings.DEFAULT_CHARSET)) + for chunk in http_response.iterator: + mod_python_req.write(chunk) def handler(req): # mod_python hooks into this function. diff --git a/django/core/handlers/wsgi.py b/django/core/handlers/wsgi.py index 23204e63a6..7541a5fd9d 100644 --- a/django/core/handlers/wsgi.py +++ b/django/core/handlers/wsgi.py @@ -172,6 +172,5 @@ class WSGIHandler(BaseHandler): response_headers = response.headers.items() for c in response.cookies.values(): response_headers.append(('Set-Cookie', c.output(header=''))) - output = [response.get_content_as_string(settings.DEFAULT_CHARSET)] start_response(status, response_headers) - return output + return response.iterator diff --git a/django/middleware/common.py b/django/middleware/common.py index aa8f4ec071..3643fce553 100644 --- a/django/middleware/common.py +++ b/django/middleware/common.py @@ -67,7 +67,7 @@ class CommonMiddleware: # Use ETags, if requested. if settings.USE_ETAGS: - etag = md5.new(response.get_content_as_string(settings.DEFAULT_CHARSET)).hexdigest() + etag = md5.new(response.content).hexdigest() if request.META.get('HTTP_IF_NONE_MATCH') == etag: response = httpwrappers.HttpResponseNotModified() else: diff --git a/django/utils/cache.py b/django/utils/cache.py index 7c7a41bafa..b888cef179 100644 --- a/django/utils/cache.py +++ b/django/utils/cache.py @@ -74,7 +74,7 @@ def patch_response_headers(response, cache_timeout=None): cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS now = datetime.datetime.utcnow() if not response.has_header('ETag'): - response['ETag'] = md5.new(response.get_content_as_string('utf8')).hexdigest() + response['ETag'] = md5.new(response.content).hexdigest() if not response.has_header('Last-Modified'): response['Last-Modified'] = now.strftime('%a, %d %b %Y %H:%M:%S GMT') if not response.has_header('Expires'): diff --git a/django/utils/httpwrappers.py b/django/utils/httpwrappers.py index 8716505f2e..9d731d23c9 100644 --- a/django/utils/httpwrappers.py +++ b/django/utils/httpwrappers.py @@ -143,14 +143,20 @@ def parse_cookie(cookie): cookiedict[key] = c.get(key).value return cookiedict -class HttpResponse: +class HttpResponse(object): "A basic HTTP response, with content and dictionary-accessed headers" def __init__(self, content='', mimetype=None): + from django.conf.settings import DEFAULT_CONTENT_TYPE, DEFAULT_CHARSET + self._charset = DEFAULT_CHARSET if not mimetype: - from django.conf.settings import DEFAULT_CONTENT_TYPE, DEFAULT_CHARSET mimetype = "%s; charset=%s" % (DEFAULT_CONTENT_TYPE, DEFAULT_CHARSET) - self.content = content - self.headers = {'Content-Type':mimetype} + if hasattr(content, '__iter__'): + self.iterator = content + self._is_string = False + else: + self.iterator = [content] + self._is_string = True + self.headers = {'Content-Type': mimetype} self.cookies = SimpleCookie() self.status_code = 200 @@ -193,25 +199,32 @@ class HttpResponse: except KeyError: pass - def get_content_as_string(self, encoding): - """ - Returns the content as a string, encoding it from a Unicode object if - necessary. - """ - if isinstance(self.content, unicode): - return self.content.encode(encoding) - return self.content + def _get_content(self): + content = ''.join(self.iterator) + if isinstance(content, unicode): + content = content.encode(self._charset) + return content + + def _set_content(self, value): + self.iterator = [value] + self._is_string = True + + content = property(_get_content, _set_content) # The remaining methods partially implement the file-like object interface. # See http://docs.python.org/lib/bltin-file-objects.html def write(self, content): - self.content += content + if not self._is_string: + raise Exception, "This %s instance is not writable" % self.__class__ + self.iterator.append(content) def flush(self): pass def tell(self): - return len(self.content) + if not self._is_string: + raise Exception, "This %s instance cannot tell its position" % self.__class__ + return sum(len(chunk) for chunk in self.iterator) class HttpResponseRedirect(HttpResponse): def __init__(self, redirect_to): diff --git a/docs/request_response.txt b/docs/request_response.txt index 818c639d9a..fc78a3459b 100644 --- a/docs/request_response.txt +++ b/docs/request_response.txt @@ -304,6 +304,10 @@ Methods Instantiates an ``HttpResponse`` object with the given page content (a string) and MIME type. The ``DEFAULT_MIME_TYPE`` is ``"text/html"``. + **New in Django development version:** ``content`` can be an iterator + instead of a string. This iterator should return strings, and those strings + will be joined together to form the content of the response. + ``__setitem__(header, value)`` Sets the given header name to the given value. Both ``header`` and ``value`` should be strings. @@ -341,7 +345,12 @@ Methods ``get_content_as_string(encoding)`` Returns the content as a Python string, encoding it from a Unicode object - if necessary. + if necessary. **Removed in Django development version.** + +``content`` + **New in Django development version.** Returns the content as a Python + string, encoding it from a Unicode object if necessary. Note this is a + property, not a method, so use ``r.content`` instead of ``r.content()``. ``write(content)``, ``flush()`` and ``tell()`` These methods make an ``HttpResponse`` instance a file-like object.