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

root/branches/cherrypy-3.0.x/cherrypy/_cptree.py

Revision 1612 (checked in by fumanchu, 2 years ago)

Promoted namespace dicts to their own class (so they can share docs).

  • 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, _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
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets