diff options
Diffstat (limited to 'webob/response.py')
-rw-r--r-- | webob/response.py | 153 |
1 files changed, 107 insertions, 46 deletions
diff --git a/webob/response.py b/webob/response.py index 37fa39f..9aba71e 100644 --- a/webob/response.py +++ b/webob/response.py @@ -1,11 +1,38 @@ -import sys, re, urlparse, zlib, struct -from datetime import datetime, date, timedelta +import re +import urlparse +import zlib +import struct + +from datetime import datetime +from datetime import timedelta from webob.headers import ResponseHeaders -from webob.cachecontrol import CacheControl, serialize_cache_control +from webob.cachecontrol import CacheControl +from webob.cachecontrol import serialize_cache_control + +from webob.byterange import ContentRange + +from webob.descriptors import deprecated_property +from webob.descriptors import list_header +from webob.descriptors import converter +from webob.descriptors import header_getter +from webob.descriptors import parse_int +from webob.descriptors import serialize_int +from webob.descriptors import parse_content_range +from webob.descriptors import serialize_content_range +from webob.descriptors import date_header +from webob.descriptors import parse_etag_response +from webob.descriptors import serialize_etag_response +from webob.descriptors import parse_int_safe +from webob.descriptors import parse_auth +from webob.descriptors import serialize_auth +from webob.descriptors import CHARSET_RE +from webob.descriptors import SCHEME_RE + +from webob.datetime_utils import parse_date_delta +from webob.datetime_utils import serialize_date_delta +from webob.datetime_utils import timedelta_to_seconds -from webob.descriptors import * -from webob.datetime_utils import * from webob.cookies import Cookie, Morsel from webob.util import status_reasons from webob.request import StringIO @@ -89,7 +116,8 @@ class Response(object): if isinstance(body, unicode): if charset is None: raise TypeError( - "You cannot set the body to a unicode value without a charset") + "You cannot set the body to a unicode value without a " + "charset") body = body.encode(charset) self._body = body if headerlist is None: @@ -118,7 +146,6 @@ class Response(object): must have a ``Content-Length``""" headerlist = [] status = fp.readline().strip() - content_length = None while 1: line = fp.readline().strip() if not line: @@ -157,7 +184,8 @@ class Response(object): # def __repr__(self): - return '<%s at 0x%x %s>' % (self.__class__.__name__, abs(id(self)), self.status) + return '<%s at 0x%x %s>' % (self.__class__.__name__, abs(id(self)), + self.status) def __str__(self, skip_body=False): parts = [self.status] @@ -206,7 +234,8 @@ class Response(object): return int(self._status.split()[0]) def _status_int__set(self, code): self._status = '%d %s' % (code, status_reasons[code]) - status_int = property(_status_int__get, _status_int__set, doc=_status_int__get.__doc__) + status_int = property(_status_int__get, _status_int__set, + doc=_status_int__get.__doc__) status_code = deprecated_property( status_int, 'status_code', 'use .status or .status_int instead', @@ -234,7 +263,8 @@ class Response(object): def _headerlist__del(self): self.headerlist = [] - headerlist = property(_headerlist__get, _headerlist__set, _headerlist__del, doc=_headerlist__get.__doc__) + headerlist = property(_headerlist__get, _headerlist__set, + _headerlist__del, doc=_headerlist__get.__doc__) def _headers__get(self): """ @@ -275,19 +305,22 @@ class Response(object): app_iter_repr = ( app_iter_repr[:30] + '...' + app_iter_repr[-10:]) raise ValueError( - 'An item of the app_iter (%s) was unicode, causing a unicode body: %r' - % (app_iter_repr, body)) + 'An item of the app_iter (%s) was unicode, causing a ' + 'unicode body: %r' % (app_iter_repr, body)) self._app_iter = None - if self._environ is not None and self._environ['REQUEST_METHOD'] == 'HEAD': + if (self._environ is not None and + self._environ['REQUEST_METHOD'] == 'HEAD'): assert len(body) == 0, "HEAD responses must be empty" elif len(body) == 0: - # if body-length is zero, we assume it's a HEAD response and leave content_length alone - pass + # if body-length is zero, we assume it's a HEAD response and + # leave content_length alone + pass # pragma: no cover (no idea why necessary, it's hit) elif self.content_length is None: self.content_length = len(body) elif self.content_length != len(body): raise AssertionError( - "Content-Length is different from actual app_iter length (%r!=%r)" + "Content-Length is different from actual app_iter length " + "(%r!=%r)" % (self.content_length, len(body)) ) return self._body @@ -295,7 +328,8 @@ class Response(object): def _body__set(self, value): if isinstance(value, unicode): raise TypeError( - "You cannot set Response.body to a unicode object (use Response.unicode_body)") + "You cannot set Response.body to a unicode object (use " + "Response.unicode_body)") if not isinstance(value, str): raise TypeError( "You can only set the body to a str (not %s)" @@ -304,7 +338,8 @@ class Response(object): if self._body or self._app_iter: self.content_md5 = None except AttributeError: - # if setting body early in initialization _body and _app_iter don't exist yet + # if setting body early in initialization _body and _app_iter + # don't exist yet pass self._body = value self.content_length = len(value) @@ -324,7 +359,8 @@ class Response(object): def _unicode_body__get(self): """ - Get/set the unicode value of the body (using the charset of the Content-Type) + Get/set the unicode value of the body (using the charset of the + Content-Type) """ if not self.charset: raise AttributeError( @@ -338,14 +374,17 @@ class Response(object): "You cannot access Response.unicode_body unless charset is set") if not isinstance(value, unicode): raise TypeError( - "You can only set Response.unicode_body to a unicode string (not %s)" % type(value)) + "You can only set Response.unicode_body to a unicode string " + "(not %s)" % type(value)) self.body = value.encode(self.charset) def _unicode_body__del(self): del self.body - unicode_body = property(_unicode_body__get, _unicode_body__set, _unicode_body__del, doc=_unicode_body__get.__doc__) - ubody = property(_unicode_body__get, _unicode_body__set, _unicode_body__del, doc="Alias for unicode_body") + unicode_body = property(_unicode_body__get, _unicode_body__set, + _unicode_body__del, doc=_unicode_body__get.__doc__) + ubody = property(_unicode_body__get, _unicode_body__set, + _unicode_body__del, doc="Alias for unicode_body") @@ -364,7 +403,8 @@ class Response(object): def _body_file__del(self): del self.body - body_file = property(_body_file__get, fdel=_body_file__del, doc=_body_file__get.__doc__) + body_file = property(_body_file__get, fdel=_body_file__del, + doc=_body_file__get.__doc__) def write(self, text): if isinstance(text, unicode): @@ -402,7 +442,8 @@ class Response(object): self.content_length = None self._app_iter = self._body = None - app_iter = property(_app_iter__get, _app_iter__set, _app_iter__del, doc=_app_iter__get.__doc__) + app_iter = property(_app_iter__get, _app_iter__set, _app_iter__del, + doc=_app_iter__get.__doc__) @@ -479,7 +520,8 @@ class Response(object): return header = self.headers.pop('Content-Type', None) if header is None: - raise AttributeError("You cannot set the charset when no content-type is defined") + raise AttributeError("You cannot set the charset when no " + "content-type is defined") match = CHARSET_RE.search(header) if match: header = header[:match.start()] + header[match.end():] @@ -496,7 +538,8 @@ class Response(object): header = header[:match.start()] + header[match.end():] self.headers['Content-Type'] = header - charset = property(_charset__get, _charset__set, _charset__del, doc=_charset__get.__doc__) + charset = property(_charset__get, _charset__set, _charset__del, + doc=_charset__get.__doc__) # @@ -569,7 +612,8 @@ class Response(object): self.headers['Content-Type'] = ct def _content_type_params__del(self): - self.headers['Content-Type'] = self.headers.get('Content-Type', '').split(';', 1)[0] + self.headers['Content-Type'] = self.headers.get( + 'Content-Type', '').split(';', 1)[0] content_type_params = property( _content_type_params__get, @@ -660,10 +704,12 @@ class Response(object): resp.headers.add('Set-Cookie', header) return resp else: - c_headers = [h for h in self.headerlist if h[0].lower() == 'set-cookie'] + c_headers = [h for h in self.headerlist if + h[0].lower() == 'set-cookie'] def repl_app(environ, start_response): def repl_start_response(status, headers, exc_info=None): - return start_response(status, headers+c_headers, exc_info=exc_info) + return start_response(status, headers+c_headers, + exc_info=exc_info) return resp(environ, repl_start_response) return repl_app @@ -681,7 +727,8 @@ class Response(object): """ value = self.headers.get('cache-control', '') if self._cache_control_obj is None: - self._cache_control_obj = CacheControl.parse(value, updates_to=self._update_cache_control, type='response') + self._cache_control_obj = CacheControl.parse( + value, updates_to=self._update_cache_control, type='response') self._cache_control_obj.header_value = value if self._cache_control_obj.header_value != value: new_obj = CacheControl.parse(value, type='response') @@ -718,7 +765,9 @@ class Response(object): else: self.headers['Cache-Control'] = value - cache_control = property(_cache_control__get, _cache_control__set, _cache_control__del, doc=_cache_control__get.__doc__) + cache_control = property( + _cache_control__get, _cache_control__set, + _cache_control__del, doc=_cache_control__get.__doc__) # @@ -772,7 +821,8 @@ class Response(object): Encode the content with the given encoding (only gzip and identity are supported). """ - assert encoding in ('identity', 'gzip'), "Unknown encoding: %r" % encoding + assert encoding in ('identity', 'gzip'), \ + "Unknown encoding: %r" % encoding if encoding == 'identity': self.decode_content() return @@ -802,7 +852,6 @@ class Response(object): gzip_f.close() f.close() else: - import zlib # Weird feature: http://bugs.python.org/issue5784 self.body = zlib.decompress(self.body, -15) self.content_encoding = None @@ -817,9 +866,9 @@ class Response(object): """ if body is None: body = self.body - try: + try: # pragma: no cover from hashlib import md5 - except ImportError: + except ImportError: # pragma: no cover from md5 import md5 md5_digest = md5(body).digest().encode('base64').replace('\n', '') self.etag = md5_digest.strip('=') @@ -853,7 +902,8 @@ class Response(object): def _request__del(self): self._request = self._environ = None - request = property(_request__get, _request__set, _request__del, doc=_request__get.__doc__) + request = property(_request__get, _request__set, _request__del, + doc=_request__get.__doc__) # @@ -876,7 +926,8 @@ class Response(object): def _environ__del(self): self._request = self._environ = None - environ = property(_environ__get, _environ__set, _environ__del, doc=_environ__get.__doc__) + environ = property(_environ__get, _environ__set, _environ__del, + doc=_environ__get.__doc__) @@ -909,7 +960,8 @@ class Response(object): new_location = urlparse.urljoin( _request_uri(environ), value) headerlist = list(headerlist) - headerlist[headerlist.index((name, value))] = (name, new_location) + idx = headerlist.index((name, value)) + headerlist[idx] = (name, new_location) break return headerlist @@ -946,21 +998,29 @@ class Response(object): iter_close(self.app_iter) body = "Requested range not satisfiable: %s" % req.range headerlist = [ - ('Content-Length', str(len(body))), - ('Content-Range', str(ContentRange(None, None, self.content_length))), + ('Content-Length', + str(len(body))), + ('Content-Range', + str(ContentRange(None, None, self.content_length))), ('Content-Type', 'text/plain'), ] + filter_headers(headerlist) - start_response('416 Requested Range Not Satisfiable', headerlist) + start_response('416 Requested Range Not Satisfiable', + headerlist) if req.method == 'HEAD': return () return [body] else: - app_iter = self.app_iter_range(content_range.start, content_range.stop) + app_iter = self.app_iter_range(content_range.start, + content_range.stop) if app_iter is not None: - assert content_range.start is not None # this should be guaranteed by Range.range_for_length(length) + assert content_range.start is not None + # above should be guaranteed by + # Range.range_for_length(length) headerlist = [ - ('Content-Length', str(content_range.stop - content_range.start)), - ('Content-Range', str(content_range)), + ('Content-Length', + str(content_range.stop - content_range.start)), + ('Content-Range', + str(content_range)), ] + filter_headers(headerlist, ('content-length',)) start_response('206 Partial Content', headerlist) if req.method == 'HEAD': @@ -1145,7 +1205,8 @@ def iter_close(iter): def gzip_app_iter(app_iter): size = 0 crc = zlib.crc32("") & 0xffffffffL - compress = zlib.compressobj(9, zlib.DEFLATED, -zlib.MAX_WBITS, zlib.DEF_MEM_LEVEL, 0) + compress = zlib.compressobj(9, zlib.DEFLATED, -zlib.MAX_WBITS, + zlib.DEF_MEM_LEVEL, 0) yield _gzip_header for item in app_iter: |