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