|
1 This demonstrates how the Response object works, and tests it at the |
|
2 same time. |
|
3 |
|
4 >>> from dtopt import ELLIPSIS |
|
5 >>> from webob import Response, UTC |
|
6 >>> from datetime import datetime |
|
7 >>> res = Response('Test', status='200 OK') |
|
8 |
|
9 This is a minimal response object. We can do things like get and set |
|
10 the body: |
|
11 |
|
12 >>> res.body |
|
13 'Test' |
|
14 >>> res.body = 'Another test' |
|
15 >>> res.body |
|
16 'Another test' |
|
17 >>> res.body = 'Another' |
|
18 >>> res.write(' test') |
|
19 >>> res.app_iter |
|
20 ['Another test'] |
|
21 >>> res.content_length |
|
22 12 |
|
23 >>> res.headers['content-length'] |
|
24 '12' |
|
25 |
|
26 Content-Length is only applied when setting the body to a string; you |
|
27 have to set it manually otherwise. There are also getters and setters |
|
28 for the various pieces: |
|
29 |
|
30 >>> res.app_iter = ['test'] |
|
31 >>> print res.content_length |
|
32 None |
|
33 >>> res.content_length = 4 |
|
34 >>> res.status |
|
35 '200 OK' |
|
36 >>> res.status_int |
|
37 200 |
|
38 >>> res.headers |
|
39 HeaderDict([('content-type', 'text/html; charset=utf8'), ('Content-Length', '4')]) |
|
40 >>> res.headerlist |
|
41 [('content-type', 'text/html; charset=utf8'), ('Content-Length', '4')] |
|
42 |
|
43 Content-type and charset are handled separately as properties, though |
|
44 they are both in the ``res.headers['content-type']`` header: |
|
45 |
|
46 >>> res.content_type |
|
47 'text/html' |
|
48 >>> res.content_type = 'text/html' |
|
49 >>> res.content_type |
|
50 'text/html' |
|
51 >>> res.charset |
|
52 'utf8' |
|
53 >>> res.charset = 'iso-8859-1' |
|
54 >>> res.charset |
|
55 'iso-8859-1' |
|
56 >>> res.content_type |
|
57 'text/html' |
|
58 >>> res.headers['content-type'] |
|
59 'text/html; charset=iso-8859-1' |
|
60 |
|
61 Cookie handling is done through methods: |
|
62 |
|
63 >>> res.set_cookie('test', 'value') |
|
64 >>> res.headers['set-cookie'] |
|
65 'test=value; Path=/' |
|
66 >>> res.set_cookie('test2', 'value2', max_age=10000) |
|
67 >>> res.headers['set-cookie'] # We only see the last header |
|
68 'test2=value2; Max-Age=10000; Path=/' |
|
69 >>> res.headers.getall('set-cookie') |
|
70 ['test=value; Path=/', 'test2=value2; Max-Age=10000; Path=/'] |
|
71 >>> res.unset_cookie('test') |
|
72 >>> res.headers.getall('set-cookie') |
|
73 ['test2=value2; Max-Age=10000; Path=/'] |
|
74 |
|
75 Most headers are available in a parsed getter/setter form through |
|
76 properties: |
|
77 |
|
78 >>> res.age = 10 |
|
79 >>> res.age, res.headers['age'] |
|
80 (10, '10') |
|
81 >>> res.allow = ['GET', 'PUT'] |
|
82 >>> res.allow, res.headers['allow'] |
|
83 (['GET', 'PUT'], 'GET, PUT') |
|
84 >>> res.cache_control |
|
85 <CacheControl ''> |
|
86 >>> print res.cache_control.max_age |
|
87 None |
|
88 >>> res.cache_control.properties['max-age'] = None |
|
89 >>> print res.cache_control.max_age |
|
90 -1 |
|
91 >>> res.cache_control.max_age = 10 |
|
92 >>> res.cache_control |
|
93 <CacheControl 'max-age=10'> |
|
94 >>> res.headers['cache-control'] |
|
95 'max-age=10' |
|
96 >>> res.cache_control.max_stale = 10 |
|
97 Traceback (most recent call last): |
|
98 ... |
|
99 AttributeError: The property max-stale only applies to request Cache-Control |
|
100 >>> res.cache_control = {} |
|
101 >>> res.cache_control |
|
102 <CacheControl ''> |
|
103 >>> res.content_encoding = 'gzip' |
|
104 >>> (res.content_encoding, res.headers['content-encoding']) |
|
105 ('gzip', 'gzip') |
|
106 >>> res.content_language = 'en' |
|
107 >>> (res.content_language, res.headers['content-language']) |
|
108 (['en'], 'en') |
|
109 >>> res.content_location = 'http://localhost:8080' |
|
110 >>> res.headers['content-location'] |
|
111 'http://localhost:8080' |
|
112 >>> res.content_range = (0, 100, 1000) |
|
113 >>> (res.content_range, res.headers['content-range']) |
|
114 (<ContentRange bytes 0-101/1000>, 'bytes 0-101/1000') |
|
115 >>> res.date = datetime(2005, 1, 1, 12, 0, tzinfo=UTC) |
|
116 >>> (res.date, res.headers['date']) |
|
117 (datetime.datetime(2005, 1, 1, 12, 0, tzinfo=UTC), 'Sat, 01 Jan 2005 12:00:00 GMT') |
|
118 >>> print res.etag |
|
119 None |
|
120 >>> res.etag = 'foo' |
|
121 >>> (res.etag, res.headers['etag']) |
|
122 ('foo', 'foo') |
|
123 >>> res.expires = res.date |
|
124 >>> res.retry_after = 120 # two minutes |
|
125 >>> res.retry_after |
|
126 datetime.datetime(...) |
|
127 >>> res.server = 'Python/foo' |
|
128 >>> res.headers['server'] |
|
129 'Python/foo' |
|
130 >>> res.vary = ['Cookie'] |
|
131 >>> (res.vary, res.headers['vary']) |
|
132 (['Cookie'], 'Cookie') |
|
133 |
|
134 The location header will try to absolutify itself if you have a |
|
135 request object attached. |
|
136 |
|
137 >>> res.location = '/test.html' |
|
138 >>> from webob import Request |
|
139 >>> res.request = Request.blank('/') |
|
140 >>> res.location |
|
141 'http://localhost/test.html' |
|
142 >>> res.request = None |
|
143 >>> res.location |
|
144 '/test.html' |
|
145 >>> res.request = Request.blank('/') |
|
146 >>> res.location = '/test2.html' |
|
147 >>> res.request = None |
|
148 >>> res.location |
|
149 'http://localhost/test2.html' |
|
150 |
|
151 There's some conditional response handling too (you have to turn on |
|
152 conditioanl_response):: |
|
153 |
|
154 >>> res = Response(conditional_response=True) |
|
155 >>> req = Request.blank('/') |
|
156 >>> res.etag = 'tag' |
|
157 >>> req.if_none_match = 'tag' |
|
158 >>> req.get_response(res) |
|
159 <Response ... 304 Not Modified> |
|
160 >>> res.etag = 'other-tag' |
|
161 >>> req.get_response(res) |
|
162 <Response ... 200 OK> |
|
163 >>> del req.if_none_match |
|
164 >>> req.if_modified_since = datetime(2005, 1, 1, 12, 1, tzinfo=UTC) |
|
165 >>> res.last_modified = datetime(2005, 1, 1, 12, 1, tzinfo=UTC) |
|
166 >>> req.get_response(res) |
|
167 <Response ... 304 Not Modified> |
|
168 >>> res.last_modified = datetime(2006, 1, 1, 12, 1, tzinfo=UTC) |
|
169 >>> req.get_response(res) |
|
170 <Response ... 200 OK> |
|
171 >>> res.last_modified = None |
|
172 >>> req.get_response(res) |
|
173 <Response ... 200 OK> |
|
174 |
|
175 Also range response:: |
|
176 |
|
177 >>> res = Response(conditional_response=True) |
|
178 >>> req = Request.blank('/') |
|
179 >>> res.body = '0123456789' |
|
180 >>> req.range = (1, 5) |
|
181 >>> result = req.get_response(res) |
|
182 >>> result.body |
|
183 '1234' |
|
184 >>> result.content_range |
|
185 <ContentRange bytes 1-6/10> |
|
186 >>> tuple(result.content_range) |
|
187 (1, 5, 10) |
|
188 >>> # Now an invalid range: |
|
189 >>> req.range = (0, 20) |
|
190 >>> str(req.range) |
|
191 'bytes=0-21' |
|
192 >>> result = req.get_response(res) |
|
193 >>> result.body |
|
194 '0123456789' |
|
195 >>> print result.content_range |
|
196 None |
|
197 |
|
198 That was easier; we'll try it with a iterator for the body:: |
|
199 |
|
200 >>> res = Response(conditional_response=True) |
|
201 >>> res.app_iter = ['01234', '567', '89'] |
|
202 >>> req = Request.blank('/') |
|
203 >>> req.range = (1, 5) |
|
204 >>> result = req.get_response(res) |
|
205 >>> # Because we don't know the length of the app_iter, this |
|
206 >>> # doesn't work: |
|
207 >>> result.body |
|
208 '0123456789' |
|
209 >>> print result.content_range |
|
210 None |
|
211 >>> req.range = (5, None) |
|
212 >>> result = req.get_response(res) |
|
213 >>> result.body |
|
214 '56789' |
|
215 >>> result.content_range |
|
216 <ContentRange bytes 5-*/10> |
|
217 >>> # If we set Content-Length then we can use it with an app_iter |
|
218 >>> res.content_length = 10 |
|
219 >>> req.range = (1, 5) |
|
220 >>> result = req.get_response(res) |
|
221 >>> result.body |
|
222 '1234' |
|
223 >>> result.content_range |
|
224 <ContentRange bytes 1-6/10> |
|
225 >>> # And trying If-modified-since |
|
226 >>> res.etag = 'foobar' |
|
227 >>> req.if_range = 'foobar' |
|
228 >>> req.if_range |
|
229 <IfRange etag=foobar, date=*> |
|
230 >>> result = req.get_response(res) |
|
231 >>> result.content_range |
|
232 <ContentRange bytes 1-6/10> |
|
233 >>> req.if_range = 'blah' |
|
234 >>> result = req.get_response(res) |
|
235 >>> result.content_range |
|
236 >>> req.if_range = datetime(2005, 1, 1, 12, 0, tzinfo=UTC) |
|
237 >>> res.last_modified = datetime(2005, 1, 1, 12, 0, tzinfo=UTC) |
|
238 >>> result = req.get_response(res) |
|
239 >>> result.content_range |
|
240 <ContentRange bytes 1-6/10> |
|
241 >>> res.last_modified = datetime(2006, 1, 1, 12, 0, tzinfo=UTC) |
|
242 >>> result = req.get_response(res) |
|
243 >>> result.content_range |
|
244 |
|
245 Some tests of exceptions:: |
|
246 |
|
247 >>> from webob import exc |
|
248 >>> res = exc.HTTPNotFound('Not found!') |
|
249 >>> res.exception.content_type = 'text/plain' |
|
250 >>> res.content_type |
|
251 'text/plain' |
|
252 >>> res = exc.HTTPNotModified() |
|
253 >>> res.headers |
|
254 HeaderDict([]) |