| This demonstrates how the Response object works, and tests it at the |
| same time. |
| |
| >>> from dtopt import ELLIPSIS |
| >>> from webob import Response, UTC |
| >>> from datetime import datetime |
| >>> res = Response('Test', status='200 OK') |
| |
| This is a minimal response object. We can do things like get and set |
| the body: |
| |
| >>> res.body |
| 'Test' |
| >>> res.body = 'Another test' |
| >>> res.body |
| 'Another test' |
| >>> res.body = 'Another' |
| >>> res.write(' test') |
| >>> res.app_iter |
| ['Another test'] |
| >>> res.content_length |
| 12 |
| >>> res.headers['content-length'] |
| '12' |
| |
| Content-Length is only applied when setting the body to a string; you |
| have to set it manually otherwise. There are also getters and setters |
| for the various pieces: |
| |
| >>> res.app_iter = ['test'] |
| >>> print res.content_length |
| None |
| >>> res.content_length = 4 |
| >>> res.status |
| '200 OK' |
| >>> res.status_int |
| 200 |
| >>> res.headers |
| HeaderDict([('content-type', 'text/html; charset=utf8'), ('Content-Length', '4')]) |
| >>> res.headerlist |
| [('content-type', 'text/html; charset=utf8'), ('Content-Length', '4')] |
| |
| Content-type and charset are handled separately as properties, though |
| they are both in the ``res.headers['content-type']`` header: |
| |
| >>> res.content_type |
| 'text/html' |
| >>> res.content_type = 'text/html' |
| >>> res.content_type |
| 'text/html' |
| >>> res.charset |
| 'utf8' |
| >>> res.charset = 'iso-8859-1' |
| >>> res.charset |
| 'iso-8859-1' |
| >>> res.content_type |
| 'text/html' |
| >>> res.headers['content-type'] |
| 'text/html; charset=iso-8859-1' |
| |
| Cookie handling is done through methods: |
| |
| >>> res.set_cookie('test', 'value') |
| >>> res.headers['set-cookie'] |
| 'test=value; Path=/' |
| >>> res.set_cookie('test2', 'value2', max_age=10000) |
| >>> res.headers['set-cookie'] # We only see the last header |
| 'test2=value2; Max-Age=10000; Path=/' |
| >>> res.headers.getall('set-cookie') |
| ['test=value; Path=/', 'test2=value2; Max-Age=10000; Path=/'] |
| >>> res.unset_cookie('test') |
| >>> res.headers.getall('set-cookie') |
| ['test2=value2; Max-Age=10000; Path=/'] |
| |
| Most headers are available in a parsed getter/setter form through |
| properties: |
| |
| >>> res.age = 10 |
| >>> res.age, res.headers['age'] |
| (10, '10') |
| >>> res.allow = ['GET', 'PUT'] |
| >>> res.allow, res.headers['allow'] |
| (['GET', 'PUT'], 'GET, PUT') |
| >>> res.cache_control |
| <CacheControl ''> |
| >>> print res.cache_control.max_age |
| None |
| >>> res.cache_control.properties['max-age'] = None |
| >>> print res.cache_control.max_age |
| -1 |
| >>> res.cache_control.max_age = 10 |
| >>> res.cache_control |
| <CacheControl 'max-age=10'> |
| >>> res.headers['cache-control'] |
| 'max-age=10' |
| >>> res.cache_control.max_stale = 10 |
| Traceback (most recent call last): |
| ... |
| AttributeError: The property max-stale only applies to request Cache-Control |
| >>> res.cache_control = {} |
| >>> res.cache_control |
| <CacheControl ''> |
| >>> res.content_encoding = 'gzip' |
| >>> (res.content_encoding, res.headers['content-encoding']) |
| ('gzip', 'gzip') |
| >>> res.content_language = 'en' |
| >>> (res.content_language, res.headers['content-language']) |
| (['en'], 'en') |
| >>> res.content_location = 'http://localhost:8080' |
| >>> res.headers['content-location'] |
| 'http://localhost:8080' |
| >>> res.content_range = (0, 100, 1000) |
| >>> (res.content_range, res.headers['content-range']) |
| (<ContentRange bytes 0-101/1000>, 'bytes 0-101/1000') |
| >>> res.date = datetime(2005, 1, 1, 12, 0, tzinfo=UTC) |
| >>> (res.date, res.headers['date']) |
| (datetime.datetime(2005, 1, 1, 12, 0, tzinfo=UTC), 'Sat, 01 Jan 2005 12:00:00 GMT') |
| >>> print res.etag |
| None |
| >>> res.etag = 'foo' |
| >>> (res.etag, res.headers['etag']) |
| ('foo', 'foo') |
| >>> res.expires = res.date |
| >>> res.retry_after = 120 # two minutes |
| >>> res.retry_after |
| datetime.datetime(...) |
| >>> res.server = 'Python/foo' |
| >>> res.headers['server'] |
| 'Python/foo' |
| >>> res.vary = ['Cookie'] |
| >>> (res.vary, res.headers['vary']) |
| (['Cookie'], 'Cookie') |
| |
| The location header will try to absolutify itself if you have a |
| request object attached. |
| |
| >>> res.location = '/test.html' |
| >>> from webob import Request |
| >>> res.request = Request.blank('/') |
| >>> res.location |
| 'http://localhost/test.html' |
| >>> res.request = None |
| >>> res.location |
| '/test.html' |
| >>> res.request = Request.blank('/') |
| >>> res.location = '/test2.html' |
| >>> res.request = None |
| >>> res.location |
| 'http://localhost/test2.html' |
| |
| There's some conditional response handling too (you have to turn on |
| conditioanl_response):: |
| |
| >>> res = Response(conditional_response=True) |
| >>> req = Request.blank('/') |
| >>> res.etag = 'tag' |
| >>> req.if_none_match = 'tag' |
| >>> req.get_response(res) |
| <Response ... 304 Not Modified> |
| >>> res.etag = 'other-tag' |
| >>> req.get_response(res) |
| <Response ... 200 OK> |
| >>> del req.if_none_match |
| >>> req.if_modified_since = datetime(2005, 1, 1, 12, 1, tzinfo=UTC) |
| >>> res.last_modified = datetime(2005, 1, 1, 12, 1, tzinfo=UTC) |
| >>> req.get_response(res) |
| <Response ... 304 Not Modified> |
| >>> res.last_modified = datetime(2006, 1, 1, 12, 1, tzinfo=UTC) |
| >>> req.get_response(res) |
| <Response ... 200 OK> |
| >>> res.last_modified = None |
| >>> req.get_response(res) |
| <Response ... 200 OK> |
| |
| Also range response:: |
| |
| >>> res = Response(conditional_response=True) |
| >>> req = Request.blank('/') |
| >>> res.body = '0123456789' |
| >>> req.range = (1, 5) |
| >>> result = req.get_response(res) |
| >>> result.body |
| '1234' |
| >>> result.content_range |
| <ContentRange bytes 1-6/10> |
| >>> tuple(result.content_range) |
| (1, 5, 10) |
| >>> # Now an invalid range: |
| >>> req.range = (0, 20) |
| >>> str(req.range) |
| 'bytes=0-21' |
| >>> result = req.get_response(res) |
| >>> result.body |
| '0123456789' |
| >>> print result.content_range |
| None |
| |
| That was easier; we'll try it with a iterator for the body:: |
| |
| >>> res = Response(conditional_response=True) |
| >>> res.app_iter = ['01234', '567', '89'] |
| >>> req = Request.blank('/') |
| >>> req.range = (1, 5) |
| >>> result = req.get_response(res) |
| >>> # Because we don't know the length of the app_iter, this |
| >>> # doesn't work: |
| >>> result.body |
| '0123456789' |
| >>> print result.content_range |
| None |
| >>> req.range = (5, None) |
| >>> result = req.get_response(res) |
| >>> result.body |
| '56789' |
| >>> result.content_range |
| <ContentRange bytes 5-*/10> |
| >>> # If we set Content-Length then we can use it with an app_iter |
| >>> res.content_length = 10 |
| >>> req.range = (1, 5) |
| >>> result = req.get_response(res) |
| >>> result.body |
| '1234' |
| >>> result.content_range |
| <ContentRange bytes 1-6/10> |
| >>> # And trying If-modified-since |
| >>> res.etag = 'foobar' |
| >>> req.if_range = 'foobar' |
| >>> req.if_range |
| <IfRange etag=foobar, date=*> |
| >>> result = req.get_response(res) |
| >>> result.content_range |
| <ContentRange bytes 1-6/10> |
| >>> req.if_range = 'blah' |
| >>> result = req.get_response(res) |
| >>> result.content_range |
| >>> req.if_range = datetime(2005, 1, 1, 12, 0, tzinfo=UTC) |
| >>> res.last_modified = datetime(2005, 1, 1, 12, 0, tzinfo=UTC) |
| >>> result = req.get_response(res) |
| >>> result.content_range |
| <ContentRange bytes 1-6/10> |
| >>> res.last_modified = datetime(2006, 1, 1, 12, 0, tzinfo=UTC) |
| >>> result = req.get_response(res) |
| >>> result.content_range |
| |
| Some tests of exceptions:: |
| |
| >>> from webob import exc |
| >>> res = exc.HTTPNotFound('Not found!') |
| >>> res.exception.content_type = 'text/plain' |
| >>> res.content_type |
| 'text/plain' |
| >>> res = exc.HTTPNotModified() |
| >>> res.headers |
| HeaderDict([]) |