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

root/branches/598-sendall/cherrypy/_cptree.py

Revision 1826 (checked in by fumanchu, 1 year ago)

Some bus/plugin improvements.

  • 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=""):
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 = {}
59    
60     def __repr__(self):
61         return "%s.%s(%r, %r)" % (self.__module__, self.__class__.__name__,
62                                   self.root, self.script_name)
63    
64     script_name__doc = """
65     The URI "mount point" for this app. A mount point is that portion of
66     the URI which is constant for all URIs that are serviced by this
67     application; it does not include scheme, host, or proxy ("virtual host")
68     portions of the URI.
69     
70     For example, if script_name is "/my/cool/app", then the URL
71     "http://www.example.com/my/cool/app/page1" might be handled by a
72     "page1" method on the root object.
73     
74     The value of script_name MUST NOT end in a slash. If the script_name
75     refers to the root of the URI, it MUST be an empty string (not "/").
76     
77     If script_name is explicitly set to None, then the script_name will be
78     provided for each call from request.wsgi_environ['SCRIPT_NAME'].
79     """
80     def _get_script_name(self):
81         if self._script_name is None:
82             # None signals that the script name should be pulled from WSGI environ.
83             return cherrypy.request.wsgi_environ['SCRIPT_NAME'].rstrip("/")
84         return self._script_name
85     def _set_script_name(self, value):
86         if value:
87             value = value.rstrip("/")
88         self._script_name = value
89     script_name = property(fget=_get_script_name, fset=_set_script_name,
90                            doc=script_name__doc)
91    
92     def merge(self, config):
93         """Merge the given config into self.config."""
94         _cpconfig.merge(self.config, config)
95        
96         # Handle namespaces specified in config.
97         self.namespaces(self.config.get("/", {}))
98    
99     def get_serving(self, local, remote, scheme, sproto):
100         """Create and return a Request and Response object."""
101         req = self.request_class(local, remote, scheme, sproto)
102         req.app = self
103        
104         for name, toolbox in self.toolboxes.iteritems():
105             req.namespaces[name] = toolbox
106        
107         resp = self.response_class()
108         cherrypy.serving.load(req, resp)
109         cherrypy.timeout_monitor.acquire()
110         cherrypy.engine.publish('acquire_thread')
111        
112         return req, resp
113    
114     def release_serving(self):
115         """Release the current serving (request and response)."""
116         req = cherrypy.serving.request
117        
118         cherrypy.timeout_monitor.release()
119        
120         try:
121             req.close()
122         except:
123             cherrypy.log(traceback=True)
124        
125         cherrypy.serving.clear()
126    
127     def __call__(self, environ, start_response):
128         return self.wsgiapp(environ, start_response)
129
130
131 class Tree(object):
132     """A registry of CherryPy applications, mounted at diverse points.
133     
134     An instance of this class may also be used as a WSGI callable
135     (WSGI application object), in which case it dispatches to all
136     mounted apps.
137     """
138    
139     apps = {}
140     apps__doc = """
141     A dict of the form {script name: application}, where "script name"
142     is a string declaring the URI mount point (no trailing slash), and
143     "application" is an instance of cherrypy.Application (or an arbitrary
144     WSGI callable if you happen to be using a WSGI server)."""
145    
146     def __init__(self):
147         self.apps = {}
148    
149     def mount(self, root, script_name="", config=None):
150         """Mount a new app from a root object, script_name, and config.
151         
152         root: an instance of a "controller class" (a collection of page
153             handler methods) which represents the root of the application.
154         script_name: a string containing the "mount point" of the application.
155             This should start with a slash, and be the path portion of the
156             URL at which to mount the given root. For example, if root.index()
157             will handle requests to "http://www.example.com:8080/dept/app1/",
158             then the script_name argument would be "/dept/app1".
159             
160             It MUST NOT end in a slash. If the script_name refers to the
161             root of the URI, it MUST be an empty string (not "/").
162         config: a file or dict containing application config.
163         """
164         # Next line both 1) strips trailing slash and 2) maps "/" -> "".
165         script_name = script_name.rstrip("/")
166        
167         if isinstance(root, Application):
168             app = root
169         else:
170             app = Application(root, script_name)
171            
172             # If mounted at "", add favicon.ico
173             if script_name == "" and root and not hasattr(root, "favicon_ico"):
174                 favicon = os.path.join(os.getcwd(), os.path.dirname(__file__),
175                                        "favicon.ico")
176                 root.favicon_ico = tools.staticfile.handler(favicon)
177        
178         if config:
179             app.merge(config)
180        
181         self.apps[script_name] = app
182        
183         return app
184    
185     def graft(self, wsgi_callable, script_name=""):
186         """Mount a wsgi callable at the given script_name."""
187         # Next line both 1) strips trailing slash and 2) maps "/" -> "".
188         script_name = script_name.rstrip("/")
189         self.apps[script_name] = wsgi_callable
190    
191     def script_name(self, path=None):
192         """The script_name of the app at the given path, or None.
193         
194         If path is None, cherrypy.request is used.
195         """
196        
197         if path is None:
198             try:
199                 path = _http.urljoin(cherrypy.request.script_name,
200                                      cherrypy.request.path_info)
201             except AttributeError:
202                 return None
203        
204         while True:
205             if path in self.apps:
206                 return path
207            
208             if path == "":
209                 return None
210            
211             # Move one node up the tree and try again.
212             path = path[:path.rfind("/")]
213    
214     def __call__(self, environ, start_response):
215         # If you're calling this, then you're probably setting SCRIPT_NAME
216         # to '' (some WSGI servers always set SCRIPT_NAME to '').
217         # Try to look up the app using the full path.
218         path = _http.urljoin(environ.get('SCRIPT_NAME', ''),
219                              environ.get('PATH_INFO', ''))
220         sn = self.script_name(path or "/")
221         if sn is None:
222             start_response('404 Not Found', [])
223             return []
224        
225         app = self.apps[sn]
226        
227         # Correct the SCRIPT_NAME and PATH_INFO environ entries.
228         environ = environ.copy()
229         environ['SCRIPT_NAME'] = sn
230         environ['PATH_INFO'] = path[len(sn.rstrip("/")):]
231         return app(environ, start_response)
232
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets