Package cherrypy :: Package test :: Module test_dynamicobjectmapping
[hide private]
[frames] | no frames]

Source Code for Module cherrypy.test.test_dynamicobjectmapping

  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   
10 -def setup_server():
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 # DynamicNodeAndMethodDispatcher example. 71 # This example exposes a fairly naive HTTP api 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 # For all but PUT methods there MUST be a valid user identified 119 # by self.id 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 # Edit the current user 142 self.user.name = name 143 return "PUT %d" % self.user.id 144 else: 145 # Make a new user with said attributes. 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
248 -class DynamicObjectMappingTest(helper.CPWebCase):
249 setup_server = staticmethod(setup_server) 250
251 - def testObjectMapping(self):
252 for url in script_names: 253 prefix = self.script_name = url 254 255 self.getPage('/') 256 self.assertBody('index') 257 258 self.getPage('/handler') 259 self.assertBody('handler') 260 261 # Dynamic dispatch will succeed here for the subnodes 262 # so the subroot gets called 263 self.getPage('/1/') 264 self.assertBody('SubRoot index') 265 266 self.getPage('/2/') 267 self.assertBody('SubRoot index') 268 269 self.getPage('/1/handler') 270 self.assertBody('SubRoot handler') 271 272 self.getPage('/2/handler') 273 self.assertBody('SubRoot handler') 274 275 # Dynamic dispatch will fail here for the subnodes 276 # so the default gets called 277 self.getPage('/asdf/') 278 self.assertBody("default ('asdf',)") 279 280 self.getPage('/asdf/asdf') 281 self.assertBody("default ('asdf', 'asdf')") 282 283 self.getPage('/asdf/handler') 284 self.assertBody("default ('asdf', 'handler')") 285 286 # Dynamic dispatch will succeed here for the subsubnodes 287 # so the subsubroot gets called 288 self.getPage('/1/1/') 289 self.assertBody('SubSubRoot index') 290 291 self.getPage('/2/2/') 292 self.assertBody('SubSubRoot index') 293 294 self.getPage('/1/1/handler') 295 self.assertBody('SubSubRoot handler') 296 297 self.getPage('/2/2/handler') 298 self.assertBody('SubSubRoot handler') 299 300 self.getPage('/2/2/dispatch') 301 self.assertBody('SubSubRoot dispatch') 302 303 # The exposed dispatch will not be called as a dispatch 304 # method. 305 self.getPage('/2/2/foo/foo') 306 self.assertBody("SubSubRoot default") 307 308 # Dynamic dispatch will fail here for the subsubnodes 309 # so the SubRoot gets called 310 self.getPage('/1/asdf/') 311 self.assertBody("SubRoot ('asdf',)") 312 313 self.getPage('/1/asdf/asdf') 314 self.assertBody("SubRoot ('asdf', 'asdf')") 315 316 self.getPage('/1/asdf/handler') 317 self.assertBody("SubRoot ('asdf', 'handler')")
318
319 - def testMethodDispatch(self):
320 # GET acts like a container 321 self.getPage("/users") 322 self.assertBody("[1, 2]") 323 self.assertHeader('Allow', 'GET, HEAD, POST') 324 325 # POST to the container URI allows creation 326 self.getPage("/users", method="POST", body="name=baz") 327 self.assertBody("POST 3") 328 self.assertHeader('Allow', 'GET, HEAD, POST') 329 330 # POST to a specific instanct URI results in a 404 331 # as the resource does not exit. 332 self.getPage("/users/5", method="POST", body="name=baz") 333 self.assertStatus(404) 334 335 # PUT to a specific instanct URI results in creation 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 # GET acts like a container 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 # Make sure POSTs update already existings resources 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 # Make sure PUTs Update already existing resources. 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 # Make sure DELETES Remove already existing resources. 367 self.getPage("/users/%d" % id, method='DELETE') 368 self.assertBody("DELETE %d" % id) 369 self.assertHeader('Allow', headers) 370 371 372 # GET acts like a container 373 self.getPage("/users") 374 self.assertBody("[]") 375 self.assertHeader('Allow', 'GET, HEAD, POST')
376
377 - def testVpathDispatch(self):
378 self.getPage("/decorated/") 379 self.assertBody("no params") 380 381 self.getPage("/decorated/hi") 382 self.assertBody("hi was not interpreted as 'a' param") 383 384 self.getPage("/decorated/yo/") 385 self.assertBody("a:yo") 386 387 self.getPage("/decorated/yo/there/") 388 self.assertBody("a:yo,b:there") 389 390 self.getPage("/decorated/yo/there/delete") 391 self.assertBody("deleting yo and there") 392 393 self.getPage("/decorated/yo/there/handled_by_dispatch/") 394 self.assertBody("custom") 395 396 self.getPage("/undecorated/blah/") 397 self.assertBody("index: blah") 398 399 self.getPage("/index_only/a/b/c/d/e/f/g/") 400 self.assertBody("IndexOnly index") 401 402 self.getPage("/parameter_test/argument2/") 403 self.assertBody("argument2")
404