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, _cprequest, _cpwsgi, tools 
  6  from cherrypy.lib import http as _http 
  7   
  8   
9 -class Application(object):
10 """A CherryPy Application. 11 12 Servers and gateways should not instantiate Request objects directly. 13 Instead, they should ask an Application object for a request object. 14 15 An instance of this class may also be used as a WSGI callable 16 (WSGI application object) for itself. 17 """ 18 19 __metaclass__ = cherrypy._AttributeDocstrings 20 21 root = None 22 root__doc = """ 23 The top-most container of page handlers for this app. Handlers should 24 be arranged in a hierarchy of attributes, matching the expected URI 25 hierarchy; the default dispatcher then searches this hierarchy for a 26 matching handler. When using a dispatcher other than the default, 27 this value may be None.""" 28 29 config = {} 30 config__doc = """ 31 A dict of {path: pathconf} pairs, where 'pathconf' is itself a dict 32 of {key: value} pairs.""" 33 34 namespaces = _cpconfig.NamespaceSet() 35 toolboxes = {'tools': cherrypy.tools} 36 37 log = None 38 log__doc = """A LogManager instance. See _cplogging.""" 39 40 wsgiapp = None 41 wsgiapp__doc = """A CPWSGIApp instance. See _cpwsgi.""" 42 43 request_class = _cprequest.Request 44 response_class = _cprequest.Response 45 46 relative_urls = False 47
48 - def __init__(self, root, script_name="", config=None):
49 self.log = _cplogging.LogManager(id(self), cherrypy.log.logger_root) 50 self.root = root 51 self.script_name = script_name 52 self.wsgiapp = _cpwsgi.CPWSGIApp(self) 53 54 self.namespaces = self.namespaces.copy() 55 self.namespaces["log"] = lambda k, v: setattr(self.log, k, v) 56 self.namespaces["wsgi"] = self.wsgiapp.namespace_handler 57 58 self.config = self.__class__.config.copy() 59 if config: 60 self.merge(config)
61
62 - def __repr__(self):
63 return "%s.%s(%r, %r)" % (self.__module__, self.__class__.__name__, 64 self.root, self.script_name)
65 66 script_name__doc = """ 67 The URI "mount point" for this app. A mount point is that portion of 68 the URI which is constant for all URIs that are serviced by this 69 application; it does not include scheme, host, or proxy ("virtual host") 70 portions of the URI. 71 72 For example, if script_name is "/my/cool/app", then the URL 73 "http://www.example.com/my/cool/app/page1" might be handled by a 74 "page1" method on the root object. 75 76 The value of script_name MUST NOT end in a slash. If the script_name 77 refers to the root of the URI, it MUST be an empty string (not "/"). 78 79 If script_name is explicitly set to None, then the script_name will be 80 provided for each call from request.wsgi_environ['SCRIPT_NAME']. 81 """
82 - def _get_script_name(self):
83 if self._script_name is None: 84 # None signals that the script name should be pulled from WSGI environ. 85 return cherrypy.request.wsgi_environ['SCRIPT_NAME'].rstrip("/") 86 return self._script_name
87 - def _set_script_name(self, value):
88 if value: 89 value = value.rstrip("/") 90 self._script_name = value
91 script_name = property(fget=_get_script_name, fset=_set_script_name, 92 doc=script_name__doc) 93
94 - def merge(self, config):
95 """Merge the given config into self.config.""" 96 _cpconfig.merge(self.config, config) 97 98 # Handle namespaces specified in config. 99 self.namespaces(self.config.get("/", {}))
100
101 - def get_serving(self, local, remote, scheme, sproto):
102 """Create and return a Request and Response object.""" 103 req = self.request_class(local, remote, scheme, sproto) 104 req.app = self 105 106 for name, toolbox in self.toolboxes.iteritems(): 107 req.namespaces[name] = toolbox 108 109 resp = self.response_class() 110 cherrypy.serving.load(req, resp) 111 cherrypy.engine.timeout_monitor.acquire() 112 cherrypy.engine.publish('acquire_thread') 113 114 return req, resp
115
116 - def release_serving(self):
117 """Release the current serving (request and response).""" 118 req = cherrypy.serving.request 119 120 cherrypy.engine.timeout_monitor.release() 121 122 try: 123 req.close() 124 except: 125 cherrypy.log(traceback=True, severity=40) 126 127 cherrypy.serving.clear()
128
129 - def __call__(self, environ, start_response):
130 return self.wsgiapp(environ, start_response)
131 132
133 -class Tree(object):
134 """A registry of CherryPy applications, mounted at diverse points. 135 136 An instance of this class may also be used as a WSGI callable 137 (WSGI application object), in which case it dispatches to all 138 mounted apps. 139 """ 140 141 apps = {} 142 apps__doc = """ 143 A dict of the form {script name: application}, where "script name" 144 is a string declaring the URI mount point (no trailing slash), and 145 "application" is an instance of cherrypy.Application (or an arbitrary 146 WSGI callable if you happen to be using a WSGI server).""" 147
148 - def __init__(self):
149 self.apps = {}
150
151 - def mount(self, root, script_name="", config=None):
152 """Mount a new app from a root object, script_name, and config. 153 154 root: an instance of a "controller class" (a collection of page 155 handler methods) which represents the root of the application. 156 This may also be an Application instance, or None if using 157 a dispatcher other than the default. 158 script_name: a string containing the "mount point" of the application. 159 This should start with a slash, and be the path portion of the 160 URL at which to mount the given root. For example, if root.index() 161 will handle requests to "http://www.example.com:8080/dept/app1/", 162 then the script_name argument would be "/dept/app1". 163 164 It MUST NOT end in a slash. If the script_name refers to the 165 root of the URI, it MUST be an empty string (not "/"). 166 config: a file or dict containing application config. 167 """ 168 # Next line both 1) strips trailing slash and 2) maps "/" -> "". 169 script_name = script_name.rstrip("/") 170 171 if isinstance(root, Application): 172 app = root 173 if script_name != "" and script_name != app.script_name: 174 raise ValueError, "Cannot specify a different script name and pass an Application instance to cherrypy.mount" 175 script_name = app.script_name 176 else: 177 app = Application(root, script_name) 178 179 # If mounted at "", add favicon.ico 180 if (script_name == "" and root is not None 181 and not hasattr(root, "favicon_ico")): 182 favicon = os.path.join(os.getcwd(), os.path.dirname(__file__), 183 "favicon.ico") 184 root.favicon_ico = tools.staticfile.handler(favicon) 185 186 if config: 187 app.merge(config) 188 189 self.apps[script_name] = app 190 191 return app
192
193 - def graft(self, wsgi_callable, script_name=""):
194 """Mount a wsgi callable at the given script_name.""" 195 # Next line both 1) strips trailing slash and 2) maps "/" -> "". 196 script_name = script_name.rstrip("/") 197 self.apps[script_name] = wsgi_callable
198
199 - def script_name(self, path=None):
200 """The script_name of the app at the given path, or None. 201 202 If path is None, cherrypy.request is used. 203 """ 204 205 if path is None: 206 try: 207 path = _http.urljoin(cherrypy.request.script_name, 208 cherrypy.request.path_info) 209 except AttributeError: 210 return None 211 212 while True: 213 if path in self.apps: 214 return path 215 216 if path == "": 217 return None 218 219 # Move one node up the tree and try again. 220 path = path[:path.rfind("/")]
221
222 - def __call__(self, environ, start_response):
223 # If you're calling this, then you're probably setting SCRIPT_NAME 224 # to '' (some WSGI servers always set SCRIPT_NAME to ''). 225 # Try to look up the app using the full path. 226 path = _http.urljoin(environ.get('SCRIPT_NAME', ''), 227 environ.get('PATH_INFO', '')) 228 sn = self.script_name(path or "/") 229 if sn is None: 230 start_response('404 Not Found', []) 231 return [] 232 233 app = self.apps[sn] 234 235 # Correct the SCRIPT_NAME and PATH_INFO environ entries. 236 environ = environ.copy() 237 environ['SCRIPT_NAME'] = sn 238 environ['PATH_INFO'] = path[len(sn.rstrip("/")):] 239 return app(environ, start_response)
240