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 -def setup_server():
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 # DynamicNodeAndMethodDispatcher example. 74 # This example exposes a fairly naive HTTP api 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 # For all but PUT methods there MUST be a valid user identified 125 # by self.id 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 # Edit the current user 149 self.user.name = name 150 return "PUT %d" % self.user.id 151 else: 152 # Make a new user with said attributes. 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
267 -class DynamicObjectMappingTest(helper.CPWebCase):
268 setup_server = staticmethod(setup_server) 269
270 - def testObjectMapping(self):
271 for url in script_names: 272 prefix = self.script_name = url 273 274 self.getPage('/') 275 self.assertBody('index') 276 277 self.getPage('/handler') 278 self.assertBody('handler') 279 280 # Dynamic dispatch will succeed here for the subnodes 281 # so the subroot gets called 282 self.getPage('/1/') 283 self.assertBody('SubRoot index') 284 285 self.getPage('/2/') 286 self.assertBody('SubRoot index') 287 288 self.getPage('/1/handler') 289 self.assertBody('SubRoot handler') 290 291 self.getPage('/2/handler') 292 self.assertBody('SubRoot handler') 293 294 # Dynamic dispatch will fail here for the subnodes 295 # so the default gets called 296 self.getPage('/asdf/') 297 self.assertBody("default ('asdf',)") 298 299 self.getPage('/asdf/asdf') 300 self.assertBody("default ('asdf', 'asdf')") 301 302 self.getPage('/asdf/handler') 303 self.assertBody("default ('asdf', 'handler')") 304 305 # Dynamic dispatch will succeed here for the subsubnodes 306 # so the subsubroot gets called 307 self.getPage('/1/1/') 308 self.assertBody('SubSubRoot index') 309 310 self.getPage('/2/2/') 311 self.assertBody('SubSubRoot index') 312 313 self.getPage('/1/1/handler') 314 self.assertBody('SubSubRoot handler') 315 316 self.getPage('/2/2/handler') 317 self.assertBody('SubSubRoot handler') 318 319 self.getPage('/2/2/dispatch') 320 self.assertBody('SubSubRoot dispatch') 321 322 # The exposed dispatch will not be called as a dispatch 323 # method. 324 self.getPage('/2/2/foo/foo') 325 self.assertBody("SubSubRoot default") 326 327 # Dynamic dispatch will fail here for the subsubnodes 328 # so the SubRoot gets called 329 self.getPage('/1/asdf/') 330 self.assertBody("SubRoot ('asdf',)") 331 332 self.getPage('/1/asdf/asdf') 333 self.assertBody("SubRoot ('asdf', 'asdf')") 334 335 self.getPage('/1/asdf/handler') 336 self.assertBody("SubRoot ('asdf', 'handler')")
337
338 - def testMethodDispatch(self):
339 # GET acts like a container 340 self.getPage("/users") 341 self.assertBody("[1, 2]") 342 self.assertHeader('Allow', 'GET, HEAD, POST') 343 344 # POST to the container URI allows creation 345 self.getPage("/users", method="POST", body="name=baz") 346 self.assertBody("POST 3") 347 self.assertHeader('Allow', 'GET, HEAD, POST') 348 349 # POST to a specific instanct URI results in a 404 350 # as the resource does not exit. 351 self.getPage("/users/5", method="POST", body="name=baz") 352 self.assertStatus(404) 353 354 # PUT to a specific instanct URI results in creation 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 # GET acts like a container 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 # Make sure POSTs update already existings resources 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 # Make sure PUTs Update already existing resources. 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 # Make sure DELETES Remove already existing resources. 388 self.getPage("/users/%d" % id, method='DELETE') 389 self.assertBody("DELETE %d" % id) 390 self.assertHeader('Allow', headers) 391 392 # GET acts like a container 393 self.getPage("/users") 394 self.assertBody("[]") 395 self.assertHeader('Allow', 'GET, HEAD, POST')
396
397 - def testVpathDispatch(self):
398 self.getPage("/decorated/") 399 self.assertBody("no params") 400 401 self.getPage("/decorated/hi") 402 self.assertBody("hi was not interpreted as 'a' param") 403 404 self.getPage("/decorated/yo/") 405 self.assertBody("a:yo") 406 407 self.getPage("/decorated/yo/there/") 408 self.assertBody("a:yo,b:there") 409 410 self.getPage("/decorated/yo/there/delete") 411 self.assertBody("deleting yo and there") 412 413 self.getPage("/decorated/yo/there/handled_by_dispatch/") 414 self.assertBody("custom") 415 416 self.getPage("/undecorated/blah/") 417 self.assertBody("index: blah") 418 419 self.getPage("/index_only/a/b/c/d/e/f/g/") 420 self.assertBody("IndexOnly index") 421 422 self.getPage("/parameter_test/argument2/") 423 self.assertBody("argument2")
424