Package cherrypy :: Module _cptree
[hide private]
[frames] | no frames]

Source Code for Module cherrypy._cptree

  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