| | 379 | class Dispatcher(object): |
|---|
| | 380 | |
|---|
| | 381 | def __call__(self, path_info): |
|---|
| | 382 | """Set handler and config for the current request.""" |
|---|
| | 383 | request = cherrypy.request |
|---|
| | 384 | func, vpath = self.find_handler(path_info) |
|---|
| | 385 | |
|---|
| | 386 | # Decode any leftover %2F in the virtual_path atoms. |
|---|
| | 387 | vpath = [x.replace("%2F", "/") for x in vpath] |
|---|
| | 388 | |
|---|
| | 389 | if func: |
|---|
| | 390 | def handler(): |
|---|
| | 391 | cherrypy.response.body = func(*vpath, **request.params) |
|---|
| | 392 | request.handler = handler |
|---|
| | 393 | else: |
|---|
| | 394 | def notfound(): |
|---|
| | 395 | raise cherrypy.NotFound() |
|---|
| | 396 | request.handler = notfound |
|---|
| | 397 | |
|---|
| | 398 | def find_handler(self, path): |
|---|
| | 399 | """Find the appropriate page handler for the given path.""" |
|---|
| | 400 | request = cherrypy.request |
|---|
| | 401 | app = request.app |
|---|
| | 402 | root = app.root |
|---|
| | 403 | |
|---|
| | 404 | # Get config for the root object/path. |
|---|
| | 405 | curpath = "" |
|---|
| | 406 | nodeconf = getattr(root, "_cp_config", {}).copy() |
|---|
| | 407 | nodeconf.update(app.conf.get("/", {})) |
|---|
| | 408 | object_trail = [('root', root, nodeconf, curpath)] |
|---|
| | 409 | |
|---|
| | 410 | node = root |
|---|
| | 411 | names = [x for x in path.strip('/').split('/') if x] + ['index'] |
|---|
| | 412 | for name in names: |
|---|
| | 413 | # map to legal Python identifiers (replace '.' with '_') |
|---|
| | 414 | objname = name.replace('.', '_') |
|---|
| | 415 | |
|---|
| | 416 | nodeconf = {} |
|---|
| | 417 | node = getattr(node, objname, None) |
|---|
| | 418 | if node is not None: |
|---|
| | 419 | # Get _cp_config attached to this node. |
|---|
| | 420 | nodeconf = getattr(node, "_cp_config", {}).copy() |
|---|
| | 421 | |
|---|
| | 422 | # Mix in values from app.conf for this path. |
|---|
| | 423 | curpath = "/".join((curpath, name)) |
|---|
| | 424 | nodeconf.update(app.conf.get(curpath, {})) |
|---|
| | 425 | |
|---|
| | 426 | # Resolve "environment" entries. This must be done node-by-node |
|---|
| | 427 | # so that a child's "environment" can override concrete settings |
|---|
| | 428 | # of a parent. However, concrete settings in this node will |
|---|
| | 429 | # override "environment" settings in the same node. |
|---|
| | 430 | env = nodeconf.get("environment") |
|---|
| | 431 | if env: |
|---|
| | 432 | for k, v in cherrypy.config.environments[env].iteritems(): |
|---|
| | 433 | if k not in nodeconf: |
|---|
| | 434 | nodeconf[k] = v |
|---|
| | 435 | |
|---|
| | 436 | object_trail.append((objname, node, nodeconf, curpath)) |
|---|
| | 437 | |
|---|
| | 438 | def set_conf(): |
|---|
| | 439 | """Set cherrypy.request.config.""" |
|---|
| | 440 | base = cherrypy.config.globalconf.copy() |
|---|
| | 441 | if 'tools.staticdir.dir' in base: |
|---|
| | 442 | base['tools.staticdir.section'] = "global" |
|---|
| | 443 | for name, obj, conf, curpath in object_trail: |
|---|
| | 444 | base.update(conf) |
|---|
| | 445 | if 'tools.staticdir.dir' in conf: |
|---|
| | 446 | base['tools.staticdir.section'] = curpath |
|---|
| | 447 | request.config = base |
|---|
| | 448 | |
|---|
| | 449 | # Try successive objects (reverse order) |
|---|
| | 450 | for i in xrange(len(object_trail) - 1, -1, -1): |
|---|
| | 451 | |
|---|
| | 452 | name, candidate, nodeconf, curpath = object_trail[i] |
|---|
| | 453 | |
|---|
| | 454 | # Try a "default" method on the current leaf. |
|---|
| | 455 | defhandler = getattr(candidate, "default", None) |
|---|
| | 456 | if callable(defhandler) and getattr(defhandler, 'exposed', False): |
|---|
| | 457 | # Insert any extra _cp_config from the default handler. |
|---|
| | 458 | conf = getattr(defhandler, "_cp_config", {}) |
|---|
| | 459 | object_trail.insert(i+1, ("default", defhandler, conf, curpath)) |
|---|
| | 460 | set_conf() |
|---|
| | 461 | return defhandler, names[i:-1] |
|---|
| | 462 | |
|---|
| | 463 | # Uncomment the next line to restrict positional params to "default". |
|---|
| | 464 | # if i < len(object_trail) - 2: continue |
|---|
| | 465 | |
|---|
| | 466 | # Try the current leaf. |
|---|
| | 467 | if callable(candidate) and getattr(candidate, 'exposed', False): |
|---|
| | 468 | set_conf() |
|---|
| | 469 | if i == len(object_trail) - 1: |
|---|
| | 470 | # We found the extra ".index". Check if the original path |
|---|
| | 471 | # had a trailing slash (otherwise, do a redirect). |
|---|
| | 472 | if not path.endswith('/'): |
|---|
| | 473 | atoms = request.browser_url.split("?", 1) |
|---|
| | 474 | newUrl = atoms.pop(0) + '/' |
|---|
| | 475 | if atoms: |
|---|
| | 476 | newUrl += "?" + atoms[0] |
|---|
| | 477 | raise cherrypy.HTTPRedirect(newUrl) |
|---|
| | 478 | return candidate, names[i:-1] |
|---|
| | 479 | |
|---|
| | 480 | # We didn't find anything |
|---|
| | 481 | set_conf() |
|---|
| | 482 | return None, [] |
|---|
| | 483 | |
|---|
| | 484 | default_dispatch = Dispatcher() |
|---|
| | 485 | |
|---|
| | 486 | |
|---|