Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

root/trunk/cherrypy/_cptree.py

Revision 1989 (checked in by fumanchu, 4 weeks ago)

Moved all bus plugins onto the engine object itself, and fixed a couple bus details along the way.

  • Property svn:eol-style set to native
Line 
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         script_name: a string containing the "mount point" of the application.
157             This should start with a slash, and be the path portion of the
158             URL at which to mount the given root. For example, if root.index()
159             will handle requests to "http://www.example.com:8080/dept/app1/",
160             then the script_name argument would be "/dept/app1".
161             
162             It MUST NOT end in a slash. If the script_name refers to the
163             root of the URI, it MUST be an empty string (not "/").
164         config: a file or dict containing application config.
165         """
166         # Next line both 1) strips trailing slash and 2) maps "/" -> "".
167         script_name = script_name.rstrip("/")
168        
169         if isinstance(root, Application):
170             app = root
171         else:
172             app = Application(root, script_name)
173            
174             # If mounted at "", add favicon.ico
175             if script_name == "" and root and not hasattr(root, "favicon_ico"):
176                 favicon = os.path.join(os.getcwd(), os.path.dirname(__file__),
177                                        "favicon.ico")
178                 root.favicon_ico = tools.staticfile.handler(favicon)
179        
180         if config:
181             app.merge(config)
182        
183         self.apps[script_name] = app
184        
185         return app
186    
187     def graft(self, wsgi_callable, script_name=""):
188         """Mount a wsgi callable at the given script_name."""
189         # Next line both 1) strips trailing slash and 2) maps "/" -> "".
190         script_name = script_name.rstrip("/")
191         self.apps[script_name] = wsgi_callable
192    
193     def script_name(self, path=None):
194         """The script_name of the app at the given path, or None.
195         
196         If path is None, cherrypy.request is used.
197         """
198        
199         if path is None:
200             try:
201                 path = _http.urljoin(cherrypy.request.script_name,
202                                      cherrypy.request.path_info)
203             except AttributeError:
204                 return None
205        
206         while True:
207             if path in self.apps:
208                 return path
209            
210             if path == "":
211                 return None
212            
213             # Move one node up the tree and try again.
214             path = path[:path.rfind("/")]
215    
216     def __call__(self, environ, start_response):
217         # If you're calling this, then you're probably setting SCRIPT_NAME
218         # to '' (some WSGI servers always set SCRIPT_NAME to '').
219         # Try to look up the app using the full path.
220         path = _http.urljoin(environ.get('SCRIPT_NAME', ''),
221                              environ.get('PATH_INFO', ''))
222         sn = self.script_name(path or "/")
223         if sn is None:
224             start_response('404 Not Found', [])
225             return []
226        
227         app = self.apps[sn]
228        
229         # Correct the SCRIPT_NAME and PATH_INFO environ entries.
230         environ = environ.copy()
231         environ['SCRIPT_NAME'] = sn
232         environ['PATH_INFO'] = path[len(sn.rstrip("/")):]
233         return app(environ, start_response)
234
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets