1 import cherrypy
2 from cherrypy._cpcompat import sorted, unicodestr
3 from cherrypy._cptree import Application
4 from cherrypy.test import helper
5
6 script_names = ["", "/foo", "/users/fred/blog", "/corp/blog"]
7
8
10 class SubSubRoot:
11
12 def index(self):
13 return "SubSubRoot index"
14 index.exposed = True
15
16 def default(self, *args):
17 return "SubSubRoot default"
18 default.exposed = True
19
20 def handler(self):
21 return "SubSubRoot handler"
22 handler.exposed = True
23
24 def dispatch(self):
25 return "SubSubRoot dispatch"
26 dispatch.exposed = True
27
28 subsubnodes = {
29 '1': SubSubRoot(),
30 '2': SubSubRoot(),
31 }
32
33 class SubRoot:
34
35 def index(self):
36 return "SubRoot index"
37 index.exposed = True
38
39 def default(self, *args):
40 return "SubRoot %s" % (args,)
41 default.exposed = True
42
43 def handler(self):
44 return "SubRoot handler"
45 handler.exposed = True
46
47 def _cp_dispatch(self, vpath):
48 return subsubnodes.get(vpath[0], None)
49
50 subnodes = {
51 '1': SubRoot(),
52 '2': SubRoot(),
53 }
54
55 class Root:
56
57 def index(self):
58 return "index"
59 index.exposed = True
60
61 def default(self, *args):
62 return "default %s" % (args,)
63 default.exposed = True
64
65 def handler(self):
66 return "handler"
67 handler.exposed = True
68
69 def _cp_dispatch(self, vpath):
70 return subnodes.get(vpath[0])
71
72
73
74
75 class User(object):
76
77 def __init__(self, id, name):
78 self.id = id
79 self.name = name
80
81 def __unicode__(self):
82 return unicode(self.name)
83
84 def __str__(self):
85 return str(self.name)
86
87 user_lookup = {
88 1: User(1, 'foo'),
89 2: User(2, 'bar'),
90 }
91
92 def make_user(name, id=None):
93 if not id:
94 id = max(*list(user_lookup.keys())) + 1
95 user_lookup[id] = User(id, name)
96 return id
97
98 class UserContainerNode(object):
99 exposed = True
100
101 def POST(self, name):
102 """
103 Allow the creation of a new Object
104 """
105 return "POST %d" % make_user(name)
106
107 def GET(self):
108 return unicodestr(sorted(user_lookup.keys()))
109
110 def dynamic_dispatch(self, vpath):
111 try:
112 id = int(vpath[0])
113 except (ValueError, IndexError):
114 return None
115 return UserInstanceNode(id)
116
117 class UserInstanceNode(object):
118 exposed = True
119
120 def __init__(self, id):
121 self.id = id
122 self.user = user_lookup.get(id, None)
123
124
125
126 if not self.user and cherrypy.request.method != 'PUT':
127 raise cherrypy.HTTPError(404)
128
129 def GET(self, *args, **kwargs):
130 """
131 Return the appropriate representation of the instance.
132 """
133 return unicodestr(self.user)
134
135 def POST(self, name):
136 """
137 Update the fields of the user instance.
138 """
139 self.user.name = name
140 return "POST %d" % self.user.id
141
142 def PUT(self, name):
143 """
144 Create a new user with the specified id, or edit it if it already
145 exists
146 """
147 if self.user:
148
149 self.user.name = name
150 return "PUT %d" % self.user.id
151 else:
152
153 return "PUT %d" % make_user(name, self.id)
154
155 def DELETE(self):
156 """
157 Delete the user specified at the id.
158 """
159 id = self.user.id
160 del user_lookup[self.user.id]
161 del self.user
162 return "DELETE %d" % id
163
164 class ABHandler:
165
166 class CustomDispatch:
167
168 def index(self, a, b):
169 return "custom"
170 index.exposed = True
171
172 def _cp_dispatch(self, vpath):
173 """Make sure that if we don't pop anything from vpath,
174 processing still works.
175 """
176 return self.CustomDispatch()
177
178 def index(self, a, b=None):
179 body = ['a:' + str(a)]
180 if b is not None:
181 body.append(',b:' + str(b))
182 return ''.join(body)
183 index.exposed = True
184
185 def delete(self, a, b):
186 return 'deleting ' + str(a) + ' and ' + str(b)
187 delete.exposed = True
188
189 class IndexOnly:
190
191 def _cp_dispatch(self, vpath):
192 """Make sure that popping ALL of vpath still shows the index
193 handler.
194 """
195 while vpath:
196 vpath.pop()
197 return self
198
199 def index(self):
200 return "IndexOnly index"
201 index.exposed = True
202
203 class DecoratedPopArgs:
204
205 """Test _cp_dispatch with @cherrypy.popargs."""
206
207 def index(self):
208 return "no params"
209 index.exposed = True
210
211 def hi(self):
212 return "hi was not interpreted as 'a' param"
213 hi.exposed = True
214 DecoratedPopArgs = cherrypy.popargs(
215 'a', 'b', handler=ABHandler())(DecoratedPopArgs)
216
217 class NonDecoratedPopArgs:
218
219 """Test _cp_dispatch = cherrypy.popargs()"""
220
221 _cp_dispatch = cherrypy.popargs('a')
222
223 def index(self, a):
224 return "index: " + str(a)
225 index.exposed = True
226
227 class ParameterizedHandler:
228
229 """Special handler created for each request"""
230
231 def __init__(self, a):
232 self.a = a
233
234 def index(self):
235 if 'a' in cherrypy.request.params:
236 raise Exception(
237 "Parameterized handler argument ended up in "
238 "request.params")
239 return self.a
240 index.exposed = True
241
242 class ParameterizedPopArgs:
243
244 """Test cherrypy.popargs() with a function call handler"""
245 ParameterizedPopArgs = cherrypy.popargs(
246 'a', handler=ParameterizedHandler)(ParameterizedPopArgs)
247
248 Root.decorated = DecoratedPopArgs()
249 Root.undecorated = NonDecoratedPopArgs()
250 Root.index_only = IndexOnly()
251 Root.parameter_test = ParameterizedPopArgs()
252
253 Root.users = UserContainerNode()
254
255 md = cherrypy.dispatch.MethodDispatcher('dynamic_dispatch')
256 for url in script_names:
257 conf = {'/': {
258 'user': (url or "/").split("/")[-2],
259 },
260 '/users': {
261 'request.dispatch': md
262 },
263 }
264 cherrypy.tree.mount(Root(), url, conf)
265
266
268 setup_server = staticmethod(setup_server)
269
337
339
340 self.getPage("/users")
341 self.assertBody("[1, 2]")
342 self.assertHeader('Allow', 'GET, HEAD, POST')
343
344
345 self.getPage("/users", method="POST", body="name=baz")
346 self.assertBody("POST 3")
347 self.assertHeader('Allow', 'GET, HEAD, POST')
348
349
350
351 self.getPage("/users/5", method="POST", body="name=baz")
352 self.assertStatus(404)
353
354
355 self.getPage("/users/5", method="PUT", body="name=boris")
356 self.assertBody("PUT 5")
357 self.assertHeader('Allow', 'DELETE, GET, HEAD, POST, PUT')
358
359
360 self.getPage("/users")
361 self.assertBody("[1, 2, 3, 5]")
362 self.assertHeader('Allow', 'GET, HEAD, POST')
363
364 test_cases = (
365 (1, 'foo', 'fooupdated', 'DELETE, GET, HEAD, POST, PUT'),
366 (2, 'bar', 'barupdated', 'DELETE, GET, HEAD, POST, PUT'),
367 (3, 'baz', 'bazupdated', 'DELETE, GET, HEAD, POST, PUT'),
368 (5, 'boris', 'borisupdated', 'DELETE, GET, HEAD, POST, PUT'),
369 )
370 for id, name, updatedname, headers in test_cases:
371 self.getPage("/users/%d" % id)
372 self.assertBody(name)
373 self.assertHeader('Allow', headers)
374
375
376 self.getPage("/users/%d" %
377 id, method='POST', body="name=%s" % updatedname)
378 self.assertBody("POST %d" % id)
379 self.assertHeader('Allow', headers)
380
381
382 self.getPage("/users/%d" %
383 id, method='PUT', body="name=%s" % updatedname)
384 self.assertBody("PUT %d" % id)
385 self.assertHeader('Allow', headers)
386
387
388 self.getPage("/users/%d" % id, method='DELETE')
389 self.assertBody("DELETE %d" % id)
390 self.assertHeader('Allow', headers)
391
392
393 self.getPage("/users")
394 self.assertBody("[]")
395 self.assertHeader('Allow', 'GET, HEAD, POST')
396
424