Add an e-mail dispatcher that can be used to send messages via the website. Add base and invitation templates that can be used with email dispatcher to send invitation emails. Please read the module doc string for more information how to use it.
Patch by: Lennard de Rijk, Pawel Solyga
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([])