Argument store added to updateEntityProperties.
This argument determines if an entity should be stored in the data model after its properties are updated.
It may be useful, for example, along with tasks (Task Queue API). One may want to make some modifications to an entity during execution of a task, but the developer is sure that at least one new task, which also wants to modify the entity, will be queued, so he or she can just update the entity without saving the changes to the data model, set the entity in memcache and the following task (which is to be executed very shortly) is to retrive the current entity from the memcache (without any expensive calls to the actual data model).
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([])