source file: /Library/Python/2.3/site-packages/CherryPy-3.0.1-py2.3.egg/cherrypy/_cptree.py
file stats: 82 lines, 54 executed: 65.9% covered
   1. """CherryPy Application and Tree objects."""
   2. 
   3. import os
   4. import cherrypy
   5. from cherrypy import _cpconfig, _cplogging, _cpwsgi, tools
   6. 
   7. 
   8. class Application(object):
   9.     """A CherryPy Application.
  10. 
  11.     An instance of this class may also be used as a WSGI callable
  12.     (WSGI application object) for itself.
  13.     """
  14. 
  15.     __metaclass__ = cherrypy._AttributeDocstrings
  16. 
  17.     root = None
  18.     root__doc = """
  19.     The top-most container of page handlers for this app. Handlers should
  20.     be arranged in a hierarchy of attributes, matching the expected URI
  21.     hierarchy; the default dispatcher then searches this hierarchy for a
  22.     matching handler. When using a dispatcher other than the default,
  23.     this value may be None."""
  24. 
  25.     config = {}
  26.     config__doc = """
  27.     A dict of {path: pathconf} pairs, where 'pathconf' is itself a dict
  28.     of {key: value} pairs."""
  29. 
  30.     namespaces = _cpconfig.NamespaceSet()
  31. 
  32.     log = None
  33.     log__doc = """A LogManager instance. See _cplogging."""
  34. 
  35.     wsgiapp = None
  36.     wsgiapp__doc = """A CPWSGIApp instance. See _cpwsgi."""
  37. 
  38.     def __init__(self, root, script_name=""):
  39.         self.log = _cplogging.LogManager(id(self), cherrypy.log.logger_root)
  40.         self.root = root
  41.         self.script_name = script_name
  42.         self.wsgiapp = _cpwsgi.CPWSGIApp(self)
  43. 
  44.         self.namespaces = self.namespaces.copy()
  45.         self.namespaces["log"] = lambda k, v: setattr(self.log, k, v)
  46.         self.namespaces["wsgi"] = self.wsgiapp.namespace_handler
  47. 
  48.         self.config = {}
  49. 
  50.     script_name__doc = """
  51.     The URI "mount point" for this app; for example, if script_name is
  52.     "/my/cool/app", then the URL "http://my.domain.tld/my/cool/app/page1"
  53.     might be handled by a "page1" method on the root object. If script_name
  54.     is explicitly set to None, then the script_name will be provided
  55.     for each call from request.wsgi_environ['SCRIPT_NAME']."""
  56.     def _get_script_name(self):
  57.         if self._script_name is None:
  58.             # None signals that the script name should be pulled from WSGI environ.
  59.             return cherrypy.request.wsgi_environ['SCRIPT_NAME']
  60.         return self._script_name
  61.     def _set_script_name(self, value):
  62.         self._script_name = value
  63.     script_name = property(fget=_get_script_name, fset=_set_script_name,
  64.                            doc=script_name__doc)
  65. 
  66.     def merge(self, config):
  67.         """Merge the given config into self.config."""
  68.         _cpconfig.merge(self.config, config)
  69. 
  70.         # Handle namespaces specified in config.
  71.         self.namespaces(self.config.get("/", {}))
  72. 
  73.     def __call__(self, environ, start_response):
  74.         return self.wsgiapp(environ, start_response)
  75. 
  76. 
  77. class Tree(object):
  78.     """A registry of CherryPy applications, mounted at diverse points.
  79. 
  80.     An instance of this class may also be used as a WSGI callable
  81.     (WSGI application object), in which case it dispatches to all
  82.     mounted apps.
  83.     """
  84. 
  85.     apps = {}
  86.     apps__doc = """
  87.     A dict of the form {script name: application}, where "script name"
  88.     is a string declaring the URI mount point (no trailing slash), and
  89.     "application" is an instance of cherrypy.Application (or an arbitrary
  90.     WSGI callable if you happen to be using a WSGI server)."""
  91. 
  92.     def __init__(self):
  93.         self.apps = {}
  94. 
  95.     def mount(self, root, script_name="", config=None):
  96.         """Mount a new app from a root object, script_name, and config."""
  97.         # Next line both 1) strips trailing slash and 2) maps "/" -> "".
  98.         script_name = script_name.rstrip("/")
  99. 
 100.         if isinstance(root, Application):
 101.             app = root
 102.         else:
 103.             app = Application(root, script_name)
 104. 
 105.             # If mounted at "", add favicon.ico
 106.             if script_name == "" and root and not hasattr(root, "favicon_ico"):
 107.                 favicon = os.path.join(os.getcwd(), os.path.dirname(__file__),
 108.                                        "favicon.ico")
 109.                 root.favicon_ico = tools.staticfile.handler(favicon)
 110. 
 111.         if config:
 112.             app.merge(config)
 113. 
 114.         self.apps[script_name] = app
 115. 
 116.         return app
 117. 
 118.     def graft(self, wsgi_callable, script_name=""):
 119.         """Mount a wsgi callable at the given script_name."""
 120.         # Next line both 1) strips trailing slash and 2) maps "/" -> "".
 121.         script_name = script_name.rstrip("/")
 122.         self.apps[script_name] = wsgi_callable
 123. 
 124.     def script_name(self, path=None):
 125.         """The script_name of the app at the given path, or None.
 126. 
 127.         If path is None, cherrypy.request is used.
 128.         """
 129. 
 130.         if path is None:
 131.             try:
 132.                 path = cherrypy.request.script_name + cherrypy.request.path_info
 133.             except AttributeError:
 134.                 return None
 135. 
 136.         while True:
 137.             if path in self.apps:
 138.                 return path
 139. 
 140.             if path == "":
 141.                 return None
 142. 
 143.             # Move one node up the tree and try again.
 144.             path = path[:path.rfind("/")]
 145. 
 146.     def __call__(self, environ, start_response):
 147.         # If you're calling this, then you're probably setting SCRIPT_NAME
 148.         # to '' (some WSGI servers always set SCRIPT_NAME to '').
 149.         # Try to look up the app using the full path.
 150.         path = environ.get('SCRIPT_NAME', '') + environ.get('PATH_INFO', '')
 151.         sn = self.script_name(path or "/")
 152.         if sn is None:
 153.             start_response('404 Not Found', [])
 154.             return []
 155. 
 156.         app = self.apps[sn]
 157. 
 158.         # Correct the SCRIPT_NAME and PATH_INFO environ entries.
 159.         environ = environ.copy()
 160.         environ['SCRIPT_NAME'] = sn
 161.         environ['PATH_INFO'] = path[len(sn.rstrip("/")):]
 162.         return app(environ, start_response)
 163.